home *** CD-ROM | disk | FTP | other *** search
/ Aminet 2 / Aminet AMIGA CDROM (1994)(Walnut Creek)[Feb 1994][W.O. 44790-1].iso / Aminet / text / misc / makeguide1_49b.lha / src / makeinfo.c < prev   
C/C++ Source or Header  |  1992-10-22  |  208KB  |  7,736 lines

  1. /* Makeinfo -- convert texinfo format files into info files.
  2.  
  3.    Copyright (C) 1987, 1992 Free Software Foundation, Inc.
  4.  
  5.    This file is part of GNU Info.
  6.  
  7.    Makeinfo is distributed in the hope that it will be useful,
  8.    but WITHOUT ANY WARRANTY.  No author or distributor accepts
  9.    responsibility to anyone for the consequences of using it or for
  10.    whether it serves any particular purpose or works at all, unless he
  11.    says so in writing.  Refer to the GNU Emacs General Public License
  12.    for full details.
  13.  
  14.    Everyone is granted permission to copy, modify and redistribute
  15.    Makeinfo, but only under the conditions described in the GNU Emacs
  16.    General Public License.   A copy of this license is supposed to
  17.    have been given to you along with GNU Emacs so you can know your
  18.    rights and responsibilities.  It should be in a file named COPYING.
  19.    Among other things, the copyright notice and this notice must be
  20.    preserved on all copies.  */
  21.  
  22. /* This is Makeinfo version 1.49.  If you change the version number of
  23.    Makeinfo, please change it here and at the lines reading:
  24.  
  25.     int major_version = 1;
  26.     int minor_version = 49;
  27.  
  28.    in the code below. */
  29.  
  30. /* You can change some of the behaviour of Makeinfo by changing the
  31.    following defines: */
  32.  
  33. /* Define INDENT_PARAGRAPHS_IN_TABLE if you want the paragraphs which
  34.    appear within an @table, @ftable, or @itemize environment to have
  35.    standard paragraph indentation.  Without this, such paragraphs have
  36.    no starting indentation. */
  37. /* #define INDENT_PARAGRAPHS_IN_TABLE */
  38.  
  39. /* Define DEFAULT_INDENTATION_INCREMENT as an integer which is the amount
  40.    that @example should increase indentation by.  This incremement is used
  41.    for all insertions which indent the enclosed text. */
  42. #define DEFAULT_INDENTATION_INCREMENT 5
  43.  
  44. /* Define PARAGRAPH_START_INDENT to be the amount of indentation that
  45.    the first lines of paragraphs receive by default, where no other
  46.    value has been specified.  Users can change this value on the command
  47.    line, with the +paragraph-indent option, or within the texinfo file,
  48.    with the @paragraphindent command. */
  49. #define PARAGRAPH_START_INDENT 3
  50.  
  51. /* Define DEFAULT_PARAGRAPH_SPACING as the number of blank lines that you
  52.    wish to appear between paragraphs.  A value of 1 creates a single blank
  53.    line between paragraphs.  Paragraphs are defined by 2 or more consecutive
  54.    newlines in the input file (i.e., one or more blank lines). */
  55. #define DEFAULT_PARAGRAPH_SPACING 1
  56.  
  57. /* **************************************************************** */
  58. /*                                                                  */
  59. /*                      Include File Declarations                   */
  60. /*                                                                  */
  61. /* **************************************************************** */
  62.  
  63. /* Indent #pragma so that older Cpp's don't try to parse it. */
  64. #if defined (_AIX)
  65.  # pragma alloca
  66. #endif /* _AIX */
  67.  
  68. #include <stdio.h>
  69. #include <sys/types.h>
  70. #include <ctype.h>
  71. #include <sys/stat.h>
  72. #include <ctype.h>
  73. #if !defined (AMIGA)
  74. #include <pwd.h>
  75. #endif
  76. #include <errno.h>
  77. #include "getopt.h"
  78.  
  79. #if defined (VMS)
  80. #  include <perror.h>
  81. #endif
  82.  
  83. #if defined (USG) || defined (VMS) || defined (_IBMR2) || defined (AMIGA)
  84. #  include <string.h>
  85. #  include <time.h>
  86. #else /* !USG && !VMS && !_IBMR2 && !AMIGA */
  87. #  include <strings.h>
  88. #  include <sys/time.h>
  89. #endif /* !USG && !VMS && !_IBMR2 && !AMIGA */
  90.  
  91. #include <fcntl.h>
  92. #include <sys/file.h>
  93.  
  94. #if defined (USG) || defined (hpux)
  95. #define bcopy(source, dest, count) memcpy (dest, source, count)
  96. char *index (s, c) char *s; { char *strchr (); return strchr (s, c); }
  97. char *rindex (s, c) char *s; { char *strrchr (); return (strrchr (s, c)); }
  98. #endif /* USG || hpux */
  99.  
  100. #if defined (__GNUC__)
  101. #  define alloca __builtin_alloca
  102. #else
  103. #  if defined (sparc)
  104. #    include <alloca.h>
  105. #  else
  106. #    if !defined (_AIX)
  107. extern char *alloca ();
  108. #    endif /* _AIX */
  109. #  endif /* !sparc */
  110. #endif /* !__GNUC__ */
  111.  
  112. /* Forward declarations. */
  113. void *xmalloc (), *xrealloc ();
  114.  
  115. /* Non-zero means that we are currently hacking the insides of an
  116.    insertion which would use a fixed width font. */
  117. static int in_fixed_width_font = 0;
  118.  
  119. /* Non-zero means that start_paragraph () MUST be called before we pay
  120.    any attention to close_paragraph () calls. */
  121. int must_start_paragraph = 0;
  122.  
  123. /* Some systems don't declare this function in pwd.h. */
  124. struct passwd *getpwnam ();
  125.  
  126.  
  127. /* **************************************************************** */
  128. /*                                                                  */
  129. /*                            Global Defines                        */
  130. /*                                                                  */
  131. /* **************************************************************** */
  132.  
  133. /* Error levels */
  134. #define NO_ERROR 0
  135. #define SYNTAX   2
  136. #define FATAL    4
  137.  
  138. /* How to allocate permanent storage for STRING. */
  139. #define savestring(x) \
  140.   ((char *)strcpy ((char *)xmalloc (1 + ((x) ? strlen (x) : 0)), \
  141.                    (x) ? (x) : ""))
  142.  
  143. /* C's standard macros don't check to make sure that the characters being
  144.    changed are within range.  So I have to check explicitly. */
  145.  
  146. /* GNU Library doesn't have toupper().  Until GNU gets this fixed, I will
  147.    have to do it. */
  148. #ifndef toupper
  149. #define toupper(c) ((c) - 32)
  150. #endif
  151.  
  152. #define coerce_to_upper(c) ((islower(c) ? toupper(c) : (c)))
  153. #define coerce_to_lower(c) ((isupper(c) ? tolower(c) : (c)))
  154.  
  155. #define control_character_bit 0x40 /* %01000000, must be off. */
  156. #define meta_character_bit 0x080/* %10000000, must be on.  */
  157. #define CTL(c) ((c) & (~control_character_bit))
  158. #define UNCTL(c) coerce_to_upper(((c)|control_character_bit))
  159. #define META(c) ((c) | (meta_character_bit))
  160. #define UNMETA(c) ((c) & (~meta_character_bit))
  161.  
  162. #define whitespace(c) (((c) == '\t') || ((c) == ' '))
  163. #define sentence_ender(c) ((c) == '.' || (c) == '?' || (c) == '!')
  164. #define cr_or_whitespace(c) (((c) == '\t') || ((c) == ' ') || ((c) == '\n'))
  165.  
  166. #ifndef isletter
  167. #define isletter(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z'))
  168. #endif
  169.  
  170. #ifndef isupper
  171. #define isupper(c) ((c) >= 'A' && (c) <= 'Z')
  172. #endif
  173.  
  174. #ifndef isdigit
  175. #define isdigit(c)  ((c) >= '0' && (c) <= '9')
  176. #endif
  177.  
  178. #ifndef digit_value
  179. #define digit_value(c) ((c) - '0')
  180. #endif
  181.  
  182. #define member(c, s) (index (s, c) != NULL)
  183.  
  184. #define COMMAND_PREFIX '@'
  185.  
  186. /* Stuff for splitting large files. */
  187. #define SPLIT_SIZE_THRESHOLD 70000      /* What's good enough for Stallman... */
  188. #define DEFAULT_SPLIT_SIZE 50000/* Is probably good enough for me. */
  189. int splitting = 1;              /* Always true for now. */
  190.  
  191. typedef int FUNCTION ();        /* So I can say FUNCTION *foo; */
  192.  
  193.  
  194. /* **************************************************************** */
  195. /*                                                                  */
  196. /*                          Global Variables                        */
  197. /*                                                                  */
  198. /* **************************************************************** */
  199.  
  200. /* Global pointer to argv[0]. */
  201. char *progname;
  202.  
  203. /* The current input file state. */
  204. char *input_filename;
  205. char *input_text;
  206. int size_of_input_text;
  207. int input_text_offset;
  208. int line_number;
  209.  
  210. #define curchar() input_text[input_text_offset]
  211.  
  212. #define command_char(c) ((!whitespace(c)) && \
  213.                          ((c) != '\n') && \
  214.                          ((c) != '{') && \
  215.                          ((c) != '}') && \
  216.                          ((c) != '='))
  217.  
  218. #define skip_whitespace() while (input_text_offset != size_of_input_text \
  219.                                  && whitespace(curchar()))\
  220.   input_text_offset++
  221.  
  222. /* Return non-zero if STRING is the text at input_text + input_text_offset,
  223.    else zero. */
  224. #define looking_at(string) \
  225.   (strncmp (input_text + input_text_offset, string, strlen (string)) == 0)
  226.  
  227. /* And writing to the output. */
  228.  
  229. /* The output file name. */
  230. char *output_filename = (char *)NULL;
  231. char *pretty_output_filename;
  232.  
  233. /* Name of the output file that the user elected to pass on the command line.
  234.    Such a name overrides any name found with the @setfilename command. */
  235. char *command_output_filename = (char *)NULL;
  236.  
  237. /* A colon separated list of directories to search for files included
  238.    with @include.  This can be controlled with the `-I' option to makeinfo. */
  239. char *include_files_path = (char *)NULL;
  240.  
  241. /* Current output stream. */
  242. FILE *output_stream;
  243.  
  244. /* Position in the output file. */
  245. int output_position;
  246.  
  247. /* Output paragraph buffer. */
  248. unsigned char *output_paragraph;
  249.  
  250. /* Offset into OUTPUT_PARAGRAPH. */
  251. int output_paragraph_offset;
  252.  
  253. /* The output paragraph "cursor" horizontal position. */
  254. int output_column = 0;
  255.  
  256. /* Non-zero means output_paragraph contains text. */
  257. int paragraph_is_open = 0;
  258.  
  259. #define INITIAL_PARAGRAPH_SPACE 5000
  260. int paragraph_buffer_len = INITIAL_PARAGRAPH_SPACE;
  261.  
  262. /* Filling.. */
  263. /* Non-zero indicates that filling will take place on long lines. */
  264. int filling_enabled = 1;
  265.  
  266. /* Non-zero means that words are not to be split, even in long lines.  This
  267.    gets changed for cm_w (). */
  268. int non_splitting_words = 0;
  269.  
  270. /* Non-zero indicates that filling a line also indents the new line. */
  271. int indented_fill = 0;
  272.  
  273. /* The column at which long lines are broken. */
  274. int fill_column = 72;
  275.  
  276. /* The amount of indentation to apply at the start of each line. */
  277. int current_indent = 0;
  278.  
  279. /* The amount of indentation to add at the starts of paragraphs.
  280.    0 means don't change existing indentation at paragraph starts.
  281.    > 0 is amount to indent new paragraphs by.
  282.    < 0 means indent to column zero by removing indentation if necessary.
  283.  
  284.    This is normally zero, but some people prefer paragraph starts to be
  285.    somewhat more indented than paragraph bodies.  A pretty value for
  286.    this is 3. */
  287. int paragraph_start_indent = PARAGRAPH_START_INDENT;
  288.  
  289. /* Non-zero means that the use of paragraph_start_indent is inhibited.
  290.    @example uses this to line up the left columns of the example text.
  291.    A negative value for this variable is incremented each time it is used.
  292.    @noindent uses this to inhibit indentation for a single paragraph.  */
  293. int inhibit_paragraph_indentation = 0;
  294.  
  295. /* Indentation that is pending insertion.  We have this for hacking lines
  296.    which look blank, but contain whitespace.  We want to treat those as
  297.    blank lines. */
  298. int pending_indent = 0;
  299.  
  300. /* The amount that indentation increases/decreases by. */
  301. int default_indentation_increment = DEFAULT_INDENTATION_INCREMENT;
  302.  
  303. /* Non-zero indicates that indentation is temporarily turned off. */
  304. int no_indent = 1;
  305.  
  306. /* Non-zero means forcing output text to be flushright. */
  307. int force_flush_right = 0;
  308.  
  309. /* Non-zero means that the footnote style for this document was set on
  310.    the command line, which overrides any other settings. */
  311. int footnote_style_preset = 0;
  312.  
  313. /* Non-zero means that we automatically number footnotes that have no
  314.    specified marker. */
  315. int number_footnotes = 1;
  316.  
  317. /* The current footnote number in this node.  Each time a new node is
  318.    started this is reset to 1. */
  319. int current_footnote_number = 1;
  320.  
  321. /* Command name in the process of being hacked. */
  322. char *command;
  323.  
  324. /* The index in our internal command table of the currently
  325.    executing command. */
  326. int command_index;
  327.  
  328. /* A stack of file information records.  If a new file is read in with
  329.    "@input", we remember the old input file state on this stack. */
  330. typedef struct fstack
  331. {
  332.   struct fstack *next;
  333.   char *filename;
  334.   char *text;
  335.   int size;
  336.   int offset;
  337.   int line_number;
  338. } FSTACK;
  339.  
  340. FSTACK *filestack = (FSTACK *) NULL;
  341.  
  342. /* Stuff for nodes. */
  343. /* The current nodes node name. */
  344. char *current_node = (char *)NULL;
  345.  
  346. /* The current nodes section level. */
  347. int current_section = 0;
  348.  
  349. /* The filename of the current input file.  This is never freed. */
  350. char *node_filename = (char *)NULL;
  351.  
  352. /* What we remember for each node. */
  353. typedef struct tentry
  354. {
  355.   struct tentry *next_ent;
  356.   char *node;           /* name of this node. */
  357.   char *prev;           /* name of "Prev:" for this node. */
  358.   char *next;           /* name of "Next:" for this node. */
  359.   char *up;             /* name of "Up:" for this node.   */
  360.   int position;         /* output file position of this node. */
  361.   int line_no;          /* defining line in source file. */
  362.   char *filename;       /* The file that this node was found in. */
  363.   int touched;          /* non-zero means this node has been referenced. */
  364.   int flags;            /* Room for growth.  Right now, contains 1 bit. */
  365. } TAG_ENTRY;
  366.  
  367. /* If node-a has a "Next" for node-b, but node-b has no "Prev" for node-a,
  368.    we turn on this flag bit in node-b's tag entry.  This means that when
  369.    it is time to validate node-b, we don't report an additional error
  370.    if there was no "Prev" field. */
  371. #define PREV_ERROR 0x1
  372. #define NEXT_ERROR 0x2
  373. #define UP_ERROR   0x4
  374. #define NO_WARN    0x8
  375. #define IS_TOP     0x10
  376.  
  377. TAG_ENTRY *tag_table = (TAG_ENTRY *) NULL;
  378.  
  379. #define HAVE_MACROS
  380. #if defined (HAVE_MACROS)
  381. /* Macro definitions for user-defined commands. */
  382. typedef struct {
  383.   char *name;                   /* Name of the macro. */
  384.   char *definition;             /* Definition text. */
  385.   char *filename;               /* File where this macro is defined. */
  386.   int lineno;                   /* Line number within FILENAME. */
  387. } MACRO_DEF;
  388.  
  389. void add_macro (), execute_macro ();
  390. MACRO_DEF *find_macro (), *delete_macro ();
  391. #endif /* HAVE_MACROS */
  392.  
  393. /* Menu reference, *note reference, and validation hacking. */
  394.  
  395. /* The various references that we know about. */
  396. enum reftype
  397. {
  398.   menu_reference, followed_reference
  399. };
  400.  
  401. /* A structure to remember references with.  A reference to a node is
  402.    either an entry in a menu, or a cross-reference made with [px]ref. */
  403. typedef struct node_ref
  404. {
  405.   struct node_ref *next;
  406.   char *node;                   /* Name of node referred to. */
  407.   char *containing_node;        /* Name of node containing this reference. */
  408.   int line_no;                  /* Line number where the reference occurs. */
  409.   int section;                  /* Section level where the reference occurs. */
  410.   char *filename;               /* Name of file where the reference occurs. */
  411.   enum reftype type;            /* Type of reference, either menu or note. */
  412. } NODE_REF;
  413.  
  414. /* The linked list of such structures. */
  415. NODE_REF *node_references = (NODE_REF *) NULL;
  416.  
  417. /* Flag which tells us whether to examine menu lines or not. */
  418. int in_menu = 0;
  419.  
  420. /* Flags controlling the operation of the program. */
  421.  
  422. /* Default is to notify users of bad choices. */
  423. int print_warnings = 1;
  424.  
  425. /* Default is to check node references. */
  426. int validating = 1;
  427.  
  428. /* Non-zero means do not output "Node: Foo" for node separations. */
  429. int no_headers = 0;
  430.  
  431. /* Number of errors that we tolerate on a given fileset. */
  432. int max_error_level = 100;
  433.  
  434. /* Maximum number of references to a single node before complaining. */
  435. int reference_warning_limit = 1000;
  436.  
  437. /* Non-zero means print out information about what is going on when it
  438.    is going on. */
  439. int verbose_mode = 0;
  440.  
  441. /* Non-zero means converting to AmigaGuide hypertext format. */
  442. int amiga_guide = 0;
  443.  
  444. /* Non-zero means we are writing an AmigaGuide button. */
  445.  
  446. int in_amiga_guide_button = 0;
  447.  
  448. /* The list of commands that we hack in texinfo.  Each one
  449.    has an associated function.  When the command is encountered in the
  450.    text, the associated function is called with START as the argument.
  451.    If the function expects arguments in braces, it remembers itself on
  452.    the stack.  When the corresponding close brace is encountered, the
  453.    function is called with END as the argument. */
  454.  
  455. #define START 0
  456. #define END 1
  457.  
  458. typedef struct brace_element
  459. {
  460.   struct brace_element *next;
  461.   FUNCTION *proc;
  462.   int pos, line;
  463. } BRACE_ELEMENT;
  464.  
  465. BRACE_ELEMENT *brace_stack = (BRACE_ELEMENT *) NULL;
  466.  
  467. /* Forward declarations. */
  468.  
  469. int insert_self (), cm_ignore_line ();
  470.  
  471. int
  472.   cm_tex (), cm_asterisk (), cm_dots (), cm_bullet (), cm_TeX (),
  473.   cm_copyright (), cm_code (), cm_samp (), cm_file (), cm_kbd (),
  474.   cm_key (), cm_ctrl (), cm_var (), cm_dfn (), cm_emph (), cm_strong (),
  475.   cm_cite (), cm_italic (), cm_bold (), cm_roman (), cm_title (), cm_w (),
  476.   cm_refill (), cm_titlefont ();
  477.  
  478. int
  479.   cm_chapter (), cm_unnumbered (), cm_appendix (), cm_top (),
  480.   cm_section (), cm_unnumberedsec (), cm_appendixsec (),
  481.   cm_subsection (), cm_unnumberedsubsec (), cm_appendixsubsec (),
  482.   cm_subsubsection (), cm_unnumberedsubsubsec (), cm_appendixsubsubsec (),
  483.   cm_heading (), cm_chapheading (), cm_subheading (), cm_subsubheading (),
  484.   cm_majorheading ();
  485.  
  486. /* All @defxxx commands map to cm_defun (). */
  487. int cm_defun ();
  488.  
  489. int
  490.   cm_node (), cm_menu (), cm_xref (), cm_ref (), cm_ftable (), cm_vtable (), cm_pxref (),
  491.   cm_inforef (), cm_quotation (), cm_display (), cm_itemize (),
  492.   cm_enumerate (), cm_table (), cm_itemx (), cm_noindent (), cm_setfilename (),
  493.   cm_br (), cm_sp (), cm_page (), cm_group (), cm_center (), cm_include (),
  494.   cm_bye (), cm_item (), cm_end (), cm_infoinclude (), cm_ifinfo (),
  495.   cm_kindex (), cm_cindex (), cm_findex (), cm_pindex (), cm_vindex (),
  496.   cm_tindex (), cm_asis (), cm_synindex (), cm_printindex (), cm_minus (),
  497.   cm_footnote (), cm_force_abbreviated_whitespace (), cm_example (),
  498.   cm_smallexample (), cm_lisp (), cm_format (), cm_exdent (), cm_defindex (),
  499.   cm_defcodeindex (), cm_sc (), cm_result (), cm_expansion (), cm_equiv (),
  500.   cm_print (), cm_error (), cm_point (), cm_today (), cm_flushleft (),
  501.   cm_flushright (), cm_smalllisp (), cm_finalout (), cm_math (),
  502.   cm_cartouche (), cm_ignore_sentence_ender ();
  503.  
  504. /* Conditionals. */
  505. int cm_set (), cm_clear (), cm_ifset (), cm_ifclear (), cm_value ();
  506.  
  507. #if defined (HAVE_MACROS)
  508. /* Define a user-defined command which is simple substitution. */
  509. int cm_macro (), cm_unmacro ();
  510. #endif /* HAVE_MACROS */
  511.  
  512. /* Options. */
  513. int cm_paragraphindent (), cm_footnotestyle ();
  514.  
  515. /* Internals. */
  516. int do_nothing (), command_name_condition ();
  517. int misplaced_brace (), cm_obsolete ();
  518.  
  519. typedef struct
  520. {
  521.   char *name;
  522.   FUNCTION *proc;
  523.   int argument_in_braces;
  524. } COMMAND;
  525.  
  526. /* Stuff for defining commands on the fly. */
  527. COMMAND **user_command_array = (COMMAND **) NULL;
  528. int user_command_array_len = 0;
  529.  
  530. #define NO_BRACE_ARGS 0
  531. #define BRACE_ARGS 1
  532.  
  533. static COMMAND CommandTable[] = {
  534.   { "!", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  535.   { "'", insert_self, NO_BRACE_ARGS },
  536.   { "*", cm_asterisk, NO_BRACE_ARGS },
  537.   { ".", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  538.   { ":", cm_force_abbreviated_whitespace, NO_BRACE_ARGS },
  539.   { "?", cm_ignore_sentence_ender, NO_BRACE_ARGS },
  540.   { "|", do_nothing, NO_BRACE_ARGS },
  541.   { "@", insert_self, NO_BRACE_ARGS },
  542.   { " ", insert_self, NO_BRACE_ARGS },
  543.   { "\n", insert_self, NO_BRACE_ARGS },
  544.   { "TeX", cm_TeX, BRACE_ARGS },
  545.   { "`", insert_self, NO_BRACE_ARGS },
  546.   { "appendix", cm_appendix, NO_BRACE_ARGS },
  547.   { "appendixsection", cm_appendixsec, NO_BRACE_ARGS },
  548.   { "appendixsec", cm_appendixsec, NO_BRACE_ARGS },
  549.   { "appendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  550.   { "appendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  551.   { "asis", cm_asis, BRACE_ARGS },
  552.   { "b", cm_bold, BRACE_ARGS },
  553.   { "br", cm_br, NO_BRACE_ARGS },
  554.   { "bullet", cm_bullet, BRACE_ARGS },
  555.   { "bye", cm_bye, NO_BRACE_ARGS },
  556.   { "c", cm_ignore_line, NO_BRACE_ARGS },
  557.   { "cartouche", cm_cartouche, NO_BRACE_ARGS },
  558.   { "center", cm_center, NO_BRACE_ARGS },
  559.   { "chapheading", cm_chapheading, NO_BRACE_ARGS },
  560.   { "chapter", cm_chapter, NO_BRACE_ARGS },
  561.   { "cindex", cm_cindex, NO_BRACE_ARGS },
  562.   { "cite", cm_cite, BRACE_ARGS },
  563.   { "clear", cm_clear, NO_BRACE_ARGS },
  564.   { "code", cm_code, BRACE_ARGS },
  565.   { "comment", cm_ignore_line, NO_BRACE_ARGS },
  566.   { "contents", do_nothing, NO_BRACE_ARGS },
  567.   { "copyright", cm_copyright, BRACE_ARGS },
  568.   { "ctrl", cm_ctrl, BRACE_ARGS },
  569.   { "defcodeindex", cm_defcodeindex, NO_BRACE_ARGS },
  570.   { "defindex", cm_defindex, NO_BRACE_ARGS },
  571.   { "dfn", cm_dfn, BRACE_ARGS },
  572.  
  573. /* The `def' commands. */
  574.   { "deffn", cm_defun, NO_BRACE_ARGS },
  575.   { "deffnx", cm_defun, NO_BRACE_ARGS },
  576.   { "defun", cm_defun, NO_BRACE_ARGS },
  577.   { "defunx", cm_defun, NO_BRACE_ARGS },
  578.   { "defmac", cm_defun, NO_BRACE_ARGS },
  579.   { "defmacx", cm_defun, NO_BRACE_ARGS },
  580.   { "defspec", cm_defun, NO_BRACE_ARGS },
  581.   { "defspecx", cm_defun, NO_BRACE_ARGS },
  582.   { "defvr", cm_defun, NO_BRACE_ARGS },
  583.   { "defvrx", cm_defun, NO_BRACE_ARGS },
  584.   { "defvar", cm_defun, NO_BRACE_ARGS },
  585.   { "defvarx", cm_defun, NO_BRACE_ARGS },
  586.   { "defopt", cm_defun, NO_BRACE_ARGS },
  587.   { "defoptx", cm_defun, NO_BRACE_ARGS },
  588.   { "deftypefn", cm_defun, NO_BRACE_ARGS },
  589.   { "deftypefnx", cm_defun, NO_BRACE_ARGS },
  590.   { "deftypefun", cm_defun, NO_BRACE_ARGS },
  591.   { "deftypefunx", cm_defun, NO_BRACE_ARGS },
  592.   { "deftypevr", cm_defun, NO_BRACE_ARGS },
  593.   { "deftypevrx", cm_defun, NO_BRACE_ARGS },
  594.   { "deftypevar", cm_defun, NO_BRACE_ARGS },
  595.   { "deftypevarx", cm_defun, NO_BRACE_ARGS },
  596.   { "defcv", cm_defun, NO_BRACE_ARGS },
  597.   { "defcvx", cm_defun, NO_BRACE_ARGS },
  598.   { "defivar", cm_defun, NO_BRACE_ARGS },
  599.   { "defivarx", cm_defun, NO_BRACE_ARGS },
  600.   { "defop", cm_defun, NO_BRACE_ARGS },
  601.   { "defopx", cm_defun, NO_BRACE_ARGS },
  602.   { "defmethod", cm_defun, NO_BRACE_ARGS },
  603.   { "defmethodx", cm_defun, NO_BRACE_ARGS },
  604.   { "deftypemethod", cm_defun, NO_BRACE_ARGS },
  605.   { "deftypemethodx", cm_defun, NO_BRACE_ARGS },
  606.   { "deftp", cm_defun, NO_BRACE_ARGS },
  607.   { "deftpx", cm_defun, NO_BRACE_ARGS },
  608. /* The end of the `def' commands. */
  609.  
  610.   { "display", cm_display, NO_BRACE_ARGS },
  611.   { "dots", cm_dots, BRACE_ARGS },
  612.   { "dmn", do_nothing, BRACE_ARGS },
  613.   { "emph", cm_emph, BRACE_ARGS },
  614.   { "end", cm_end, NO_BRACE_ARGS },
  615.   { "enumerate", cm_enumerate, NO_BRACE_ARGS },
  616.   { "equiv", cm_equiv, BRACE_ARGS },
  617.   { "error", cm_error, BRACE_ARGS },
  618.   { "example", cm_example, NO_BRACE_ARGS },
  619.   { "exdent", cm_exdent, NO_BRACE_ARGS },
  620.   { "expansion", cm_expansion, BRACE_ARGS },
  621.   { "file", cm_file, BRACE_ARGS },
  622.   { "findex", cm_findex, NO_BRACE_ARGS },
  623.   { "finalout", do_nothing, NO_BRACE_ARGS },
  624.   { "flushleft", cm_flushleft, NO_BRACE_ARGS },
  625.   { "flushright", cm_flushright, NO_BRACE_ARGS },
  626.   { "format", cm_format, NO_BRACE_ARGS },
  627.   { "ftable", cm_ftable, NO_BRACE_ARGS },
  628.   { "group", cm_group, NO_BRACE_ARGS },
  629.   { "heading", cm_heading, NO_BRACE_ARGS },
  630.   { "headings", cm_ignore_line, NO_BRACE_ARGS },
  631.   { "i", cm_italic, BRACE_ARGS },
  632.   { "iappendix", cm_appendix, NO_BRACE_ARGS },
  633.   { "iappendixsection", cm_appendixsec, NO_BRACE_ARGS },
  634.   { "iappendixsec", cm_appendixsec, NO_BRACE_ARGS },
  635.   { "iappendixsubsec", cm_appendixsubsec, NO_BRACE_ARGS },
  636.   { "iappendixsubsubsec", cm_appendixsubsubsec, NO_BRACE_ARGS },
  637.   { "ichapter", cm_chapter, NO_BRACE_ARGS },
  638.   { "ifclear", cm_ifclear, NO_BRACE_ARGS },
  639.   { "ifinfo", cm_ifinfo, NO_BRACE_ARGS },
  640.   { "ifset", cm_ifset, NO_BRACE_ARGS },
  641.   { "iftex", command_name_condition, NO_BRACE_ARGS },
  642.   { "ignore", command_name_condition, NO_BRACE_ARGS },
  643.   { "include", cm_include, NO_BRACE_ARGS },
  644.   { "inforef", cm_inforef, BRACE_ARGS },
  645.   { "input", cm_include, NO_BRACE_ARGS },
  646.   { "isection", cm_section, NO_BRACE_ARGS },
  647.   { "isubsection", cm_subsection, NO_BRACE_ARGS },
  648.   { "isubsubsection", cm_subsubsection, NO_BRACE_ARGS },
  649.   { "item", cm_item, NO_BRACE_ARGS },
  650.   { "itemize", cm_itemize, NO_BRACE_ARGS },
  651.   { "itemx", cm_itemx, NO_BRACE_ARGS },
  652.   { "iunnumbered", cm_unnumbered, NO_BRACE_ARGS },
  653.   { "iunnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  654.   { "iunnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  655.   { "iunnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  656.   { "kbd", cm_kbd, BRACE_ARGS },
  657.   { "key", cm_key, BRACE_ARGS },
  658.   { "kindex", cm_kindex, NO_BRACE_ARGS },
  659.   { "lisp", cm_lisp, NO_BRACE_ARGS },
  660.   { "macro", cm_macro, NO_BRACE_ARGS },
  661.   { "majorheading", cm_majorheading, NO_BRACE_ARGS },
  662.   { "math", cm_math, BRACE_ARGS },
  663.   { "menu", cm_menu, NO_BRACE_ARGS },
  664.   { "minus", cm_minus, BRACE_ARGS },
  665.   { "need", cm_ignore_line, NO_BRACE_ARGS },
  666.   { "node", cm_node, NO_BRACE_ARGS },
  667.   { "noindent", cm_noindent, NO_BRACE_ARGS },
  668.   { "nwnode", cm_node, NO_BRACE_ARGS },
  669.   { "overfullrule", cm_ignore_line, NO_BRACE_ARGS },
  670.   { "page", do_nothing, NO_BRACE_ARGS },
  671.   { "pindex", cm_pindex, NO_BRACE_ARGS },
  672.   { "point", cm_point, BRACE_ARGS },
  673.   { "print", cm_print, BRACE_ARGS },
  674.   { "printindex", cm_printindex, NO_BRACE_ARGS },
  675.   { "pxref", cm_pxref, BRACE_ARGS },
  676.   { "quotation", cm_quotation, NO_BRACE_ARGS },
  677.   { "r", cm_roman, BRACE_ARGS },
  678.   { "ref", cm_ref, BRACE_ARGS },
  679.   { "refill", cm_refill, NO_BRACE_ARGS },
  680.   { "result", cm_result, BRACE_ARGS },
  681.   { "samp", cm_samp, BRACE_ARGS },
  682.   { "sc", cm_sc, BRACE_ARGS },
  683.   { "section", cm_section, NO_BRACE_ARGS },
  684.   { "set", cm_set, NO_BRACE_ARGS },
  685.   { "setchapternewpage", cm_ignore_line, NO_BRACE_ARGS },
  686.   { "setchapterstyle", cm_ignore_line, NO_BRACE_ARGS },
  687.   { "setfilename", cm_setfilename, NO_BRACE_ARGS },
  688.   { "settitle", cm_ignore_line, NO_BRACE_ARGS },
  689.   { "shortcontents", do_nothing, NO_BRACE_ARGS },
  690.   { "smallbook", cm_ignore_line, NO_BRACE_ARGS },
  691.   { "smallexample", cm_smallexample, NO_BRACE_ARGS },
  692.   { "smalllisp", cm_smalllisp, NO_BRACE_ARGS },
  693.   { "sp", cm_sp, NO_BRACE_ARGS },
  694.   { "strong", cm_strong, BRACE_ARGS },
  695.   { "subheading", cm_subheading, NO_BRACE_ARGS },
  696.   { "subsection", cm_subsection, NO_BRACE_ARGS },
  697.   { "subsubheading", cm_subsubheading, NO_BRACE_ARGS },
  698.   { "subsubsection", cm_subsubsection, NO_BRACE_ARGS },
  699.   { "summarycontents", do_nothing, NO_BRACE_ARGS },
  700.   { "syncodeindex", cm_synindex, NO_BRACE_ARGS },
  701.   { "synindex", cm_synindex, NO_BRACE_ARGS },
  702.   { "t", cm_title, BRACE_ARGS },
  703.   { "table", cm_table, NO_BRACE_ARGS },
  704.   { "tex", command_name_condition, NO_BRACE_ARGS },
  705.   { "tindex", cm_tindex, NO_BRACE_ARGS },
  706.   { "titlefont", cm_titlefont, BRACE_ARGS },
  707.   { "titlepage", command_name_condition, NO_BRACE_ARGS },
  708.   { "titlespec", command_name_condition, NO_BRACE_ARGS },
  709.   { "today", cm_today, BRACE_ARGS },
  710.   { "top", cm_top, NO_BRACE_ARGS  },
  711.   { "unmacro", cm_unmacro, NO_BRACE_ARGS },
  712.   { "unnumbered", cm_unnumbered, NO_BRACE_ARGS },
  713.   { "unnumberedsec", cm_unnumberedsec, NO_BRACE_ARGS },
  714.   { "unnumberedsubsec", cm_unnumberedsubsec, NO_BRACE_ARGS },
  715.   { "unnumberedsubsubsec", cm_unnumberedsubsubsec, NO_BRACE_ARGS },
  716.   { "value", cm_value, BRACE_ARGS },
  717.   { "var", cm_var, BRACE_ARGS },
  718.   { "vindex", cm_vindex, NO_BRACE_ARGS },
  719.   { "vtable", cm_vtable, NO_BRACE_ARGS },
  720.   { "w", cm_w, BRACE_ARGS },
  721.   { "xref", cm_xref, BRACE_ARGS },
  722.   { "{", insert_self, NO_BRACE_ARGS },
  723.   { "}", insert_self, NO_BRACE_ARGS },
  724.  
  725.   /* Some obsoleted commands. */
  726.   { "infotop", cm_obsolete, NO_BRACE_ARGS },
  727.   { "infounnumbered", cm_obsolete, NO_BRACE_ARGS },
  728.   { "infounnumberedsec", cm_obsolete, NO_BRACE_ARGS },
  729.   { "infounnumberedsubsec", cm_obsolete, NO_BRACE_ARGS },
  730.   { "infounnumberedsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  731.   { "infoappendix", cm_obsolete, NO_BRACE_ARGS },
  732.   { "infoappendixsec", cm_obsolete, NO_BRACE_ARGS },
  733.   { "infoappendixsubsec", cm_obsolete, NO_BRACE_ARGS },
  734.   { "infoappendixsubsubsec", cm_obsolete, NO_BRACE_ARGS },
  735.   { "infochapter", cm_obsolete, NO_BRACE_ARGS },
  736.   { "infosection", cm_obsolete, NO_BRACE_ARGS },
  737.   { "infosubsection", cm_obsolete, NO_BRACE_ARGS },
  738.   { "infosubsubsection", cm_obsolete, NO_BRACE_ARGS },
  739.  
  740.   /* Now @include does what this was supposed to. */
  741.   { "infoinclude", cm_infoinclude, NO_BRACE_ARGS },
  742.   { "footnote", cm_footnote, NO_BRACE_ARGS}, /* self-arg eater */
  743.   { "footnotestyle", cm_footnotestyle, NO_BRACE_ARGS },
  744.   { "paragraphindent", cm_paragraphindent, NO_BRACE_ARGS },
  745.  
  746.   {(char *) NULL, (FUNCTION *) NULL}, NO_BRACE_ARGS};
  747.  
  748. int major_version = 1;
  749. int minor_version = 49;
  750.  
  751. struct option long_options[] =
  752. {
  753.   { "error-limit", 1, 0, 'e' },                 /* formerly -el */
  754.   { "fill-column", 1, 0, 'f' },                 /* formerly -fc */
  755.   { "footnote-style", 1, 0, 's' },              /* formerly -ft */
  756.   { "no-headers", 0, &no_headers, 1 },          /* Do not output Node: foo */
  757.   { "no-pointer-validate", 0, &validating, 0 }, /* formerly -nv */
  758.   { "no-validate", 0, &validating, 0 },         /* formerly -nv */
  759.   { "no-split", 0, &splitting, 0 },             /* formerly -ns */
  760.   { "no-warn", 0, &print_warnings, 0 },         /* formerly -nw */
  761.   { "number-footnotes", 0, &number_footnotes, 1 },
  762.   { "no-number-footnotes", 0, &number_footnotes, 0 },
  763.   { "output", 1, 0, 'o' },
  764.   { "paragraph-indent", 1, 0, 'p' },            /* formerly -pi */
  765.   { "reference-limit", 1, 0, 'r' },             /* formerly -rl */
  766.   { "verbose", 0, &verbose_mode, 1 },           /* formerly -verbose */
  767.   { "version", 0, 0, 'V' },
  768.   { "amiga", 0, &amiga_guide, 1},               /* convert to AmigaGuide® */
  769.   {NULL, 0, NULL, 0}
  770. };
  771.  
  772. /* Values for calling handle_variable_internal (). */
  773. #define SET     1
  774. #define CLEAR   2
  775. #define IFSET   3
  776. #define IFCLEAR 4
  777.  
  778. /* **************************************************************** */
  779. /*                                                                  */
  780. /*                      Main ()  Start of code                      */
  781. /*                                                                  */
  782. /* **************************************************************** */
  783.  
  784. /* For each file mentioned in the command line, process it, turning
  785.    texinfo commands into wonderfully formatted output text. */
  786. main (argc, argv)
  787.      int argc;
  788.      char **argv;
  789. {
  790.   extern int errors_printed;
  791.   char *filename_part ();
  792.   int c, ind;
  793.  
  794.   /* The name of this program is the last filename in argv[0]. */
  795.   progname = filename_part (argv[0]);
  796.  
  797.   /* Parse argument flags from the input line. */
  798.   while ((c = getopt_long
  799.           (argc, argv, "D:U:I:f:o:p:e:r:s:V", long_options, &ind))
  800.          != EOF)
  801.     {
  802.       if (c == 0 && long_options[ind].flag == 0)
  803.         c = long_options[ind].val;
  804.  
  805.       switch (c)
  806.         {
  807.           /* User specified variable to set or clear? */
  808.         case 'D':
  809.         case 'U':
  810.           handle_variable_internal ((c == 'D') ? SET : CLEAR, optarg);
  811.           break;
  812.  
  813.           /* User specified include file path? */
  814.         case 'I':
  815.           if (!include_files_path)
  816.             include_files_path = savestring (".");
  817.  
  818.           include_files_path = (char *)
  819.             xrealloc (include_files_path,
  820.                       2 + strlen (include_files_path) + strlen (optarg));
  821.           strcat (include_files_path, ":");
  822.           strcat (include_files_path, optarg);
  823.           break;
  824.  
  825.           /* User specified fill_column? */
  826.         case 'f':
  827.           if (sscanf (optarg, "%d", &fill_column) != 1)
  828.             usage ();
  829.           break;
  830.  
  831.           /* User specified output file? */
  832.         case 'o':
  833.           command_output_filename = savestring (optarg);
  834.           break;
  835.  
  836.           /* User specified paragraph indent (paragraph_start_index)? */
  837.         case 'p':
  838.           if (set_paragraph_indent (optarg) < 0)
  839.             usage ();
  840.           break;
  841.  
  842.           /* User specified error level? */
  843.         case 'e':
  844.           if (sscanf (optarg, "%d", &max_error_level) != 1)
  845.             usage ();
  846.           break;
  847.  
  848.           /* User specified reference warning limit? */
  849.         case 'r':
  850.           if (sscanf (optarg, "%d", &reference_warning_limit) != 1)
  851.             usage ();
  852.           break;
  853.  
  854.           /* User specified footnote style? */
  855.         case 's':
  856.           if (set_footnote_style (optarg) < 0)
  857.             usage ();
  858.           footnote_style_preset = 1;
  859.           break;
  860.  
  861.  
  862.           /* User requested version info? */
  863.         case 'V':
  864.           print_version_info ();
  865.           exit (NO_ERROR);
  866.           break;
  867.  
  868.         case '?':
  869.           usage ();
  870.         }
  871.     }
  872.  
  873.   if (optind == argc)
  874.     usage ();
  875.   else if (verbose_mode)
  876.     print_version_info ();
  877.  
  878.   /* if --amiga option is specified, do not split large files */
  879.   if (amiga_guide)
  880.      splitting=0;
  881.  
  882.   /* Remaining arguments are file names of texinfo files.
  883.      Convert them, one by one. */
  884.   while (optind != argc)
  885.     convert (argv[optind++]);
  886.  
  887.   if (errors_printed)
  888.     exit (SYNTAX);
  889.   else
  890.     exit (NO_ERROR);
  891. }
  892.  
  893. /* Display the version info of this invocation of Makeinfo. */
  894. print_version_info ()
  895. {
  896.   fprintf (stderr, "This is GNU Makeinfo version %d.%d.\n",
  897.            major_version, minor_version);
  898. }
  899.  
  900. /* **************************************************************** */
  901. /*                                                                  */
  902. /*                      Generic Utilities                           */
  903. /*                                                                  */
  904. /* **************************************************************** */
  905.  
  906. /* Just like malloc, but kills the program in case of fatal error. */
  907. void *
  908. xmalloc (nbytes)
  909.      int nbytes;
  910. {
  911.   void *temp = (void *) malloc (nbytes);
  912.  
  913.   if (nbytes && temp == (void *)NULL)
  914.     memory_error ("xmalloc", nbytes);
  915.  
  916.   return (temp);
  917. }
  918.  
  919. /* Like realloc (), but barfs if there isn't enough memory. */
  920. void *
  921. xrealloc (pointer, nbytes)
  922.      void *pointer;
  923.      int nbytes;
  924. {
  925.   void *temp;
  926.  
  927.   if (!pointer)
  928.     temp = (void *)xmalloc (nbytes);
  929.   else
  930.     temp = (void *)realloc (pointer, nbytes);
  931.  
  932.   if (nbytes && !temp)
  933.     memory_error ("xrealloc", nbytes);
  934.  
  935.   return (temp);
  936. }
  937.  
  938. memory_error (callers_name, bytes_wanted)
  939.      char *callers_name;
  940.      int bytes_wanted;
  941. {
  942.   char printable_string[80];
  943.  
  944.   sprintf (printable_string,
  945.            "Virtual memory exhausted in %s ()!  Needed %d bytes.",
  946.            callers_name, bytes_wanted);
  947.  
  948.   error (printable_string);
  949.   abort ();
  950. }
  951.  
  952. /* Tell the user how to use this program. */
  953. usage ()
  954. {
  955.   fprintf (stderr, "Usage: %s [options] texinfo-file...\n\
  956. \n\
  957. This program accepts as input files of texinfo commands and text\n\
  958. and outputs a file suitable for reading with GNU Info.\n\
  959. \n\
  960. The options are:\n\
  961. `-I DIR'              to add DIR to the directory search list for including\n\
  962.                       files with the `@include' command.\n\
  963. -D VAR                to define a variable, as with `@set'.\n\
  964. -U VAR                to undefine a variable, as with `@clear'.\n\
  965. `--no-validate'       to suppress node cross reference validation.\n\
  966. `--no-warn'           to suppress warning messages (errors are still output).\n\
  967. `--no-split'          to suppress the splitting of large files.\n\
  968. `--no-headers'        to suppress the output of Node: Foo headers.\n\
  969. `--verbose'           to print information about what is being done.\n\
  970. `--version'           to print the version number of Makeinfo.\n\
  971. `--output FILE' or `-o FILE'\n\
  972.                       to specify the output file.  When you specify the\n\
  973.                       output file in this way, any `@setfilename' in the\n\
  974.                       input file is ignored.\n\
  975. `--paragraph-indent NUM'\n\
  976.                       to set the paragraph indent to NUM (default %d).\n\
  977. `--fill-column NUM'   to set the filling column to NUM (default %d).\n\
  978. `--error-limit NUM'   to set the error limit to NUM (default %d).\n\
  979. `--reference-limit NUM'\n\
  980.                       to set the reference warning limit to NUM (default %d).\n\
  981. `--footnote-style STYLE'\n\
  982.                       to set the footnote style to STYLE.  STYLE should\n\
  983.                       either be `separate' to place footnotes in their own\n\
  984.                       node, or `end', to place the footnotes at the end of\n\
  985.                       the node in which they are defined (the default).\n\
  986. `--amiga'             to convert to AmigaGuide® hypertext format \n\n",
  987.            progname, paragraph_start_indent,
  988.            fill_column, max_error_level, reference_warning_limit);
  989.   exit (FATAL);
  990. }
  991.  
  992. /* **************************************************************** */
  993. /*                                                                  */
  994. /*                      Manipulating Lists                          */
  995. /*                                                                  */
  996. /* **************************************************************** */
  997.  
  998. typedef struct generic_list {
  999.   struct generic_list *next;
  1000. } GENERIC_LIST;
  1001.  
  1002. /* Reverse the chain of structures in LIST.  Output the new head
  1003.    of the chain.  You should always assign the output value of this
  1004.    function to something, or you will lose the chain. */
  1005. GENERIC_LIST *
  1006. reverse_list (list)
  1007.      register GENERIC_LIST *list;
  1008. {
  1009.   register GENERIC_LIST *next;
  1010.   register GENERIC_LIST *prev = (GENERIC_LIST *) NULL;
  1011.  
  1012.   while (list)
  1013.     {
  1014.       next = list->next;
  1015.       list->next = prev;
  1016.       prev = list;
  1017.       list = next;
  1018.     }
  1019.   return (prev);
  1020. }
  1021.  
  1022.  
  1023. /* **************************************************************** */
  1024. /*                                                                  */
  1025. /*                      Pushing and Popping Files                   */
  1026. /*                                                                  */
  1027. /* **************************************************************** */
  1028.  
  1029. /* Find and load the file named FILENAME.  Return a pointer to
  1030.    the loaded file, or NULL if it can't be loaded. */
  1031. char *
  1032. find_and_load (filename)
  1033.      char *filename;
  1034. {
  1035.   struct stat fileinfo;
  1036.   int file = -1, n, i, count = 0;
  1037.   char *fullpath, *result, *get_file_info_in_path ();
  1038.  
  1039.   result = fullpath = (char *)NULL;
  1040.  
  1041.   fullpath = get_file_info_in_path (filename, include_files_path, &fileinfo);
  1042.  
  1043.   if (!fullpath)
  1044.     goto error_exit;
  1045.  
  1046.   filename = fullpath;
  1047.  
  1048.   file = open (filename, O_RDONLY);
  1049.   if (file < 0)
  1050.     goto error_exit;
  1051.  
  1052.   /* Load the file. */
  1053.   result = (char *)xmalloc (fileinfo.st_size);
  1054.  
  1055.   /* VMS stat lies about the st_size value.  The actual number of
  1056.      readable bytes is always less than this value.  The arcane
  1057.      mysteries of VMS/RMS are too much to probe, so this hack
  1058.     suffices to make things work. */
  1059. #if defined (VMS)
  1060.   while ((n = read (file, result + count, fileinfo.st_size)) > 0)
  1061.     count += n;
  1062.   if (n == -1)
  1063. #else /* !VMS */
  1064.     count = fileinfo.st_size;
  1065.     if (read (file, result, fileinfo.st_size) != fileinfo.st_size)
  1066. #endif /* !VMS */
  1067.   error_exit:
  1068.     {
  1069.       if (result)
  1070.         free (result);
  1071.  
  1072.       if (fullpath)
  1073.         free (fullpath);
  1074.  
  1075.       if (file != -1)
  1076.         close (file);
  1077.  
  1078.       return ((char *) NULL);
  1079.     }
  1080.   close (file);
  1081.  
  1082.   /* Set the globals to the new file. */
  1083.   input_text = result;
  1084.   size_of_input_text = fileinfo.st_size;
  1085.   input_filename = savestring (fullpath);
  1086.   node_filename = savestring (fullpath);
  1087.   input_text_offset = 0;
  1088.   line_number = 1;
  1089.   return (result);
  1090. }
  1091.  
  1092. /* Save the state of the current input file. */
  1093. pushfile ()
  1094. {
  1095.   FSTACK *newstack = (FSTACK *) xmalloc (sizeof (FSTACK));
  1096.   newstack->filename = input_filename;
  1097.   newstack->text = input_text;
  1098.   newstack->size = size_of_input_text;
  1099.   newstack->offset = input_text_offset;
  1100.   newstack->line_number = line_number;
  1101.   newstack->next = filestack;
  1102.  
  1103.   filestack = newstack;
  1104.   push_node_filename ();
  1105. }
  1106.  
  1107. /* Make the current file globals be what is on top of the file stack. */
  1108. popfile ()
  1109. {
  1110.   extern int executing_string;
  1111.   FSTACK *temp = filestack;
  1112.  
  1113.   if (!filestack)
  1114.     abort ();                   /* My fault.  I wonder what I did? */
  1115.  
  1116.   /* Make sure that commands with braces have been satisfied. */
  1117.   if (!executing_string)
  1118.     discard_braces ();
  1119.  
  1120.   /* Get the top of the stack into the globals. */
  1121.   input_filename = filestack->filename;
  1122.   input_text = filestack->text;
  1123.   size_of_input_text = filestack->size;
  1124.   input_text_offset = filestack->offset;
  1125.   line_number = filestack->line_number;
  1126.  
  1127.   /* Pop the stack. */
  1128.   filestack = filestack->next;
  1129.   free (temp);
  1130.   pop_node_filename ();
  1131. }
  1132.  
  1133. /* Flush all open files on the file stack. */
  1134. flush_file_stack ()
  1135. {
  1136.   while (filestack)
  1137.     {
  1138.       free (input_filename);
  1139.       free (input_text);
  1140.       popfile ();
  1141.     }
  1142. }
  1143.  
  1144. int node_filename_stack_index = 0;
  1145. int node_filename_stack_size = 0;
  1146. char **node_filename_stack = (char **)NULL;
  1147.  
  1148. push_node_filename ()
  1149. {
  1150.   if (node_filename_stack_index + 1 > node_filename_stack_size)
  1151.     {
  1152.       if (!node_filename_stack)
  1153.         node_filename_stack =
  1154.           (char **)xmalloc ((node_filename_stack_size += 10)
  1155.                             * sizeof (char *));
  1156.       else
  1157.         node_filename_stack =
  1158.           (char **)xrealloc (node_filename_stack,
  1159.                              (node_filename_stack_size + 10)
  1160.                              * sizeof (char *));
  1161.     }
  1162.  
  1163.   node_filename_stack[node_filename_stack_index] = node_filename;
  1164.   node_filename_stack_index++;
  1165. }
  1166.  
  1167. pop_node_filename ()
  1168. {
  1169.   node_filename = node_filename_stack[--node_filename_stack_index];
  1170. }
  1171.  
  1172. /* Return just the simple part of the filename; i.e. the
  1173.    filename without the path information, or extensions.
  1174.    This conses up a new string. */
  1175. char *
  1176. filename_part (filename)
  1177.      char *filename;
  1178. {
  1179.   register int i = strlen (filename) - 1;
  1180.  
  1181. #if !defined (AMIGA)
  1182.  
  1183.   while (i && filename[i] != '/')
  1184.     i--;
  1185.   if (filename[i] == '/')
  1186.     i++;
  1187.  
  1188. #else /* AMIGA */
  1189.  
  1190.   while (i && filename[i] != '/' && filename[i] != ':')
  1191.     i--;
  1192.   if (filename[i] == '/' || filename[i] == ':')
  1193.     i++;
  1194.  
  1195. #endif /* AMIGA */
  1196.  
  1197. #if defined (REMOVE_OUTPUT_EXTENSIONS)
  1198.   result = savestring (&filename[i]);
  1199.  
  1200.   /* See if there is an extension to remove.  If so, remove it. */
  1201.   if (rindex (result, '.'))
  1202.     *(rindex (result, '.')) = '\0';
  1203.   return (result);
  1204. #else
  1205.   return (savestring (&filename[i]));
  1206. #endif /* REMOVE_OUTPUT_EXTENSIONS */
  1207. }
  1208.  
  1209. /* Return the pathname part of filename.  This can be NULL. */
  1210. char *
  1211. pathname_part (filename)
  1212.      char *filename;
  1213. {
  1214.   char *expand_filename ();
  1215.   char *result = (char *) NULL;
  1216.   register int i;
  1217.  
  1218.   filename = expand_filename (filename, "");
  1219.  
  1220.   i = strlen (filename) - 1;
  1221.  
  1222. #if !defined (AMIGA)
  1223.  
  1224.   while (i && filename[i] != '/')
  1225.     i--;
  1226.   if (filename[i] == '/')
  1227.     i++;
  1228.  
  1229. #else /* AMIGA */
  1230.  
  1231.   while (i && filename[i] != '/' && filename[i] != ':')
  1232.     i--;
  1233.   if (filename[i] == '/' || filename[i] == ':')
  1234.     i++;
  1235.  
  1236. #endif /* AMIGA */
  1237.  
  1238.   if (i)
  1239.     {
  1240.       result = (char *)xmalloc (1 + i);
  1241.       strncpy (result, filename, i);
  1242.       result[i] = '\0';
  1243.     }
  1244.   free (filename);
  1245.   return (result);
  1246. }
  1247.  
  1248. /* Return the expansion of FILENAME. */
  1249. char *
  1250. expand_filename (filename, input_name)
  1251.      char *filename, *input_name;
  1252. {
  1253.   char *full_pathname ();
  1254.  
  1255. #if !defined(AMIGA)
  1256.  
  1257.   filename = full_pathname (filename);
  1258.  
  1259.   if (filename[0] == '.')
  1260.     return (filename);
  1261.  
  1262.   if (filename[0] != '/' && input_name[0] == '/')
  1263.     {
  1264.       /* Make it so that relative names work. */
  1265.       char *result;
  1266.       int i = strlen (input_name) - 1;
  1267.  
  1268.       result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
  1269.       strcpy (result, input_name);
  1270.  
  1271.       while (result[i] != '/' && i)
  1272.         i--;
  1273.  
  1274.       if (result[i] == '/')
  1275.         i++;
  1276.  
  1277.       strcpy (&result[i], filename);
  1278.       free (filename);
  1279.       return (result);
  1280.     }
  1281.   return (filename);
  1282.  
  1283. #else /* AMIGA */
  1284.  
  1285.   int i = 0;
  1286.   char *result;
  1287.  
  1288.   filename = full_pathname (filename);
  1289.  
  1290.   while(filename[i] && filename[i] != ':')
  1291.     i++;
  1292.  
  1293.   if (!*input_name || filename[i] == ':') return(filename);
  1294.  
  1295.   i = strlen (input_name) - 1;
  1296.  
  1297.   result = (char *)xmalloc (1 + strlen (input_name) + strlen (filename));
  1298.   strcpy (result, input_name);
  1299.  
  1300.   while (i && result[i] != '/' && result[i] != ':')
  1301.     i--;
  1302.   if (result[i] == '/' || result[i] == ':')
  1303.     i++;
  1304.  
  1305.   strcpy (&result[i], filename);
  1306.   free (filename);
  1307.   return (result);
  1308.  
  1309. #endif /* AMIGA */
  1310. }
  1311.  
  1312. /* Return the full path to FILENAME. */
  1313. char *
  1314. full_pathname (filename)
  1315.      char *filename;
  1316. {
  1317. #if !defined(AMIGA)
  1318.  
  1319.   int initial_character;
  1320.  
  1321.   if (filename && (initial_character = *filename))
  1322.     {
  1323.       if (initial_character == '/')
  1324.         return (savestring (filename));
  1325.       if (initial_character != '~')
  1326.         {
  1327.           return (savestring (filename));
  1328.         }
  1329.       else
  1330.         {
  1331.           if (filename[1] == '/')
  1332.             {
  1333.               /* Return the concatenation of HOME and the rest of the string. */
  1334.               char *temp_home;
  1335.               char *temp_name;
  1336.  
  1337.               temp_home = (char *) getenv ("HOME");
  1338.               temp_name = (char *)xmalloc (strlen (&filename[2])
  1339.                                            + 1
  1340.                                            + temp_home ? strlen (temp_home)
  1341.                                            : 0);
  1342.               if (temp_home)
  1343.                 strcpy (temp_name, temp_home);
  1344.  
  1345.               strcat (temp_name, &filename[2]);
  1346.               return (temp_name);
  1347.             }
  1348.           else
  1349.             {
  1350.               struct passwd *user_entry;
  1351.               int i, c;
  1352.               char *username = (char *)xmalloc (257);
  1353.               char *temp_name;
  1354.  
  1355.               for (i = 1; c = filename[i]; i++)
  1356.                 {
  1357.                   if (c == '/')
  1358.                     break;
  1359.                   else
  1360.                     username[i - 1] = c;
  1361.                 }
  1362.               if (c)
  1363.                 username[i - 1] = '\0';
  1364.  
  1365.  
  1366.               user_entry = getpwnam (username);
  1367.  
  1368.               if (!user_entry)
  1369.                 return (savestring (filename));
  1370.  
  1371.               temp_name = (char *)xmalloc (1 + strlen (user_entry->pw_dir)
  1372.                                            + strlen (&filename[i]));
  1373.               strcpy (temp_name, user_entry->pw_dir);
  1374.               strcat (temp_name, &filename[i]);
  1375.               return (temp_name);
  1376.             }
  1377.         }
  1378.     }
  1379.   else
  1380.     {
  1381.       return (savestring (filename));
  1382.     }
  1383.  
  1384. #else /* AMIGA */
  1385.  
  1386.     return (savestring (filename));
  1387.  
  1388. #endif /* AMIGA */
  1389. }
  1390.  
  1391. /* **************************************************************** */
  1392. /*                                                                  */
  1393. /*                      Error Handling                              */
  1394. /*                                                                  */
  1395. /* **************************************************************** */
  1396.  
  1397. /* Number of errors encountered. */
  1398. int errors_printed = 0;
  1399.  
  1400. /* Print the last error gotten from the file system. */
  1401. fs_error (filename)
  1402.      char *filename;
  1403. {
  1404.   remember_error ();
  1405.   perror (filename);
  1406.   return (0);
  1407. }
  1408.  
  1409. /* Print an error message, and return false. */
  1410. error (format, arg1, arg2, arg3, arg4, arg5)
  1411.      char *format;
  1412. {
  1413.   remember_error ();
  1414.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1415.   fprintf (stderr, "\n");
  1416.   return ((int) 0);
  1417. }
  1418.  
  1419. /* Just like error (), but print the line number as well. */
  1420. line_error (format, arg1, arg2, arg3, arg4, arg5)
  1421.      char *format;
  1422. {
  1423.   remember_error ();
  1424.   fprintf (stderr, "%s:%d: ", input_filename, line_number);
  1425.   fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1426.   fprintf (stderr, ".\n");
  1427.   return ((int) 0);
  1428. }
  1429.  
  1430. warning (format, arg1, arg2, arg3, arg4, arg5)
  1431.      char *format;
  1432. {
  1433.   if (print_warnings)
  1434.     {
  1435.       fprintf (stderr, "%s:%d: Warning: ", input_filename, line_number);
  1436.       fprintf (stderr, format, arg1, arg2, arg3, arg4, arg5);
  1437.       fprintf (stderr, ".\n");
  1438.     }
  1439.   return ((int) 0);
  1440. }
  1441.  
  1442. /* Remember that an error has been printed.  If this is the first
  1443.    error printed, then tell them which program is printing them.
  1444.    If more than max_error_level have been printed, then exit the
  1445.    program. */
  1446. remember_error ()
  1447. {
  1448.   errors_printed++;
  1449.   if (max_error_level && (errors_printed > max_error_level))
  1450.     {
  1451.       fprintf (stderr, "Too many errors!  Gave up.\n");
  1452.       flush_file_stack ();
  1453.       cm_bye ();
  1454.       exit (1);
  1455.     }
  1456. }
  1457.  
  1458.  
  1459. /* **************************************************************** */
  1460. /*                                                                  */
  1461. /*                      Hacking Tokens and Strings                  */
  1462. /*                                                                  */
  1463. /* **************************************************************** */
  1464.  
  1465. /* Return the next token as a string pointer.  We cons the
  1466.    string. */
  1467. char *
  1468. read_token ()
  1469. {
  1470.   int i, character;
  1471.   char *result;
  1472.  
  1473.   /* If the first character to be read is self-delimiting, then that
  1474.      is the command itself. */
  1475.   character = curchar ();
  1476.   if (self_delimiting (character))
  1477.     {
  1478.       input_text_offset++;
  1479.       result = savestring (" ");
  1480.       *result = character;
  1481.       return (result);
  1482.     }
  1483.  
  1484.   for (i = 0; ((input_text_offset != size_of_input_text)
  1485.                && (character = curchar ())
  1486.                && command_char (character));
  1487.        i++, input_text_offset++);
  1488.   result = (char *)xmalloc (i + 1);
  1489.   bcopy (&input_text[input_text_offset - i], result, i);
  1490.   result[i] = '\0';
  1491.   return (result);
  1492. }
  1493.  
  1494. /* Return non-zero if CHARACTER is self-delimiting. */
  1495. int
  1496. self_delimiting (character)
  1497.      int character;
  1498. {
  1499.   return (member (character, "{}:.@*'`,!?; \n"));
  1500. }
  1501.  
  1502. /* Clear whitespace from the front and end of string. */
  1503. canon_white (string)
  1504.      char *string;
  1505. {
  1506.   int len = strlen (string);
  1507.   int x;
  1508.  
  1509.   if (!len)
  1510.     return;
  1511.  
  1512.   for (x = 0; x < len; x++)
  1513.     {
  1514.       if (!whitespace (string[x]))
  1515.         {
  1516.           strcpy (string, string + x);
  1517.           break;
  1518.         }
  1519.     }
  1520.   len = strlen (string);
  1521.   if (len)
  1522.     len--;
  1523.   while (len > -1 && cr_or_whitespace (string[len]))
  1524.     len--;
  1525.   string[len + 1] = '\0';
  1526. }
  1527.  
  1528. /* Bash STRING, replacing all whitespace with just one space. */
  1529. fix_whitespace (string)
  1530.      char *string;
  1531. {
  1532.   char *temp = (char *)xmalloc (strlen (string) + 1);
  1533.   int string_index = 0;
  1534.   int temp_index = 0;
  1535.   int c;
  1536.  
  1537.   canon_white (string);
  1538.  
  1539.   while (string[string_index])
  1540.     {
  1541.       c = temp[temp_index++] = string[string_index++];
  1542.  
  1543.       if (c == ' ' || c == '\n' || c == '\t')
  1544.         {
  1545.           temp[temp_index - 1] = ' ';
  1546.           while ((c = string[string_index]) && (c == ' ' ||
  1547.                                                 c == '\t' ||
  1548.                                                 c == '\n'))
  1549.             string_index++;
  1550.         }
  1551.     }
  1552.   temp[temp_index] = '\0';
  1553.   strcpy (string, temp);
  1554.   free (temp);
  1555. }
  1556.  
  1557. /* Discard text until the desired string is found.  The string is
  1558.    included in the discarded text. */
  1559. discard_until (string)
  1560.      char *string;
  1561. {
  1562.   int temp = search_forward (string, input_text_offset);
  1563.  
  1564.   int tt = (temp < 0) ? size_of_input_text : temp + strlen (string);
  1565.   int from = input_text_offset;
  1566.  
  1567.   /* Find out what line we are on. */
  1568.   while (from != tt)
  1569.     if (input_text[from++] == '\n')
  1570.       line_number++;
  1571.  
  1572.   if (temp < 0)
  1573.     {
  1574.       input_text_offset = size_of_input_text - strlen (string);
  1575.  
  1576.       if (strcmp (string, "\n") != 0)
  1577.         {
  1578.           line_error ("Expected `%s'", string);
  1579.           return;
  1580.         }
  1581.     }
  1582.   else
  1583.     input_text_offset = temp;
  1584.  
  1585.   input_text_offset += strlen (string);
  1586. }
  1587.  
  1588. /* Read characters from the file until we are at MATCH.
  1589.    Place the characters read into STRING.
  1590.    On exit input_text_offset is after the match string.
  1591.    Return the offset where the string starts. */
  1592. int
  1593. get_until (match, string)
  1594.      char *match, **string;
  1595. {
  1596.   int len, current_point, x, new_point, tem;
  1597.  
  1598.   current_point = x = input_text_offset;
  1599.   new_point = search_forward (match, input_text_offset);
  1600.  
  1601.   if (new_point < 0)
  1602.     new_point = size_of_input_text;
  1603.   len = new_point - current_point;
  1604.  
  1605.   /* Keep track of which line number we are at. */
  1606.   tem = new_point + (strlen (match) - 1);
  1607.   while (x != tem)
  1608.     if (input_text[x++] == '\n')
  1609.       line_number++;
  1610.  
  1611.   *string = (char *)xmalloc (len + 1);
  1612.  
  1613.   bcopy (&input_text[current_point], *string, len);
  1614.   (*string)[len] = '\0';
  1615.  
  1616.   /* Now leave input_text_offset in a consistent state. */
  1617.   input_text_offset = tem;
  1618.  
  1619.   if (input_text_offset > size_of_input_text)
  1620.     input_text_offset = size_of_input_text;
  1621.  
  1622.   return (new_point);
  1623. }
  1624.  
  1625. /* Read characters from the file until we are at MATCH or end of line.
  1626.    Place the characters read into STRING.  */
  1627. get_until_in_line (match, string)
  1628.      char *match, **string;
  1629. {
  1630.   int real_bottom, temp;
  1631.  
  1632.   real_bottom = size_of_input_text;
  1633.   temp = search_forward ("\n", input_text_offset);
  1634.  
  1635.   if (temp < 0)
  1636.     temp = size_of_input_text;
  1637.  
  1638.   size_of_input_text = temp;
  1639.   get_until (match, string);
  1640.   size_of_input_text = real_bottom;
  1641. }
  1642.  
  1643. get_rest_of_line (string)
  1644.      char **string;
  1645. {
  1646.   get_until ("\n", string);
  1647.   canon_white (*string);
  1648.  
  1649.   if (curchar () == '\n')       /* as opposed to the end of the file... */
  1650.     {
  1651.       line_number++;
  1652.       input_text_offset++;
  1653.     }
  1654. }
  1655.  
  1656. /* Backup the input pointer to the previous character, keeping track
  1657.    of the current line number. */
  1658. backup_input_pointer ()
  1659. {
  1660.   if (input_text_offset)
  1661.     {
  1662.       input_text_offset--;
  1663.       if (curchar () == '\n')
  1664.         line_number--;
  1665.     }
  1666. }
  1667.  
  1668. /* Read characters from the file until we are at MATCH or closing brace.
  1669.    Place the characters read into STRING.  */
  1670. get_until_in_braces (match, string)
  1671.      char *match, **string;
  1672. {
  1673.   int i, brace = 0;
  1674.   int match_len = strlen (match);
  1675.   char *temp;
  1676.  
  1677.   for (i = input_text_offset; i < size_of_input_text; i++)
  1678.     {
  1679.       if (input_text[i] == '{')
  1680.         brace++;
  1681.       else if (input_text[i] == '}')
  1682.         brace--;
  1683.       else if (input_text[i] == '\n')
  1684.         line_number++;
  1685.  
  1686.       if (brace < 0 ||
  1687.           (brace == 0 && strncmp (input_text + i, match, match_len) == 0))
  1688.         break;
  1689.     }
  1690.  
  1691.   match_len = i - input_text_offset;
  1692.   temp = (char *)xmalloc (2 + match_len);
  1693.   strncpy (temp, input_text + input_text_offset, match_len);
  1694.   temp[match_len] = '\0';
  1695.   input_text_offset = i;
  1696.   *string = temp;
  1697. }
  1698.  
  1699. /* **************************************************************** */
  1700. /*                                                                  */
  1701. /*                      Converting the File                         */
  1702. /*                                                                  */
  1703. /* **************************************************************** */
  1704.  
  1705. /* Convert the file named by NAME.  The output is saved on the file
  1706.    named as the argument to the @setfilename command. */
  1707. static char *suffixes[] = {
  1708.   "",
  1709.   ".texinfo",
  1710.   ".texi",
  1711.   ".txinfo",
  1712.   (char *)NULL
  1713. };
  1714.  
  1715. convert (name)
  1716.      char *name;
  1717. {
  1718.   char *real_output_filename, *expand_filename (), *filename_part ();
  1719.   char *filename = (char *)xmalloc (strlen (name) + 50);
  1720.   register int i;
  1721.  
  1722.   init_tag_table ();
  1723.   init_indices ();
  1724.   init_internals ();
  1725.   init_paragraph ();
  1726.  
  1727.   /* Try to load the file specified by NAME.  If the file isn't found, and
  1728.      there is no suffix in NAME, then try NAME.texinfo, and NAME.texi. */
  1729.   for (i = 0; suffixes[i]; i++)
  1730.     {
  1731.       strcpy (filename, name);
  1732.       strcat (filename, suffixes[i]);
  1733.  
  1734.       if (find_and_load (filename))
  1735.         break;
  1736.  
  1737.       if (!suffixes[i][0] && rindex (filename, '.'))
  1738.         {
  1739.           fs_error (filename);
  1740.           free (filename);
  1741.           return;
  1742.         }
  1743.     }
  1744.  
  1745.   if (!suffixes[i])
  1746.     {
  1747.       fs_error (name);
  1748.       free (filename);
  1749.       return;
  1750.     }
  1751.  
  1752.   input_filename = filename;
  1753.  
  1754.   /* Search this file looking for the special string which starts conversion.
  1755.      Once found, we may truly begin. */
  1756.  
  1757.   input_text_offset = search_forward ("@setfilename", 0);
  1758.  
  1759.   if (input_text_offset < 0)
  1760.     {
  1761.       if (!command_output_filename)
  1762.         {
  1763.           error ("No `@setfilename' found in `%s'", name);
  1764.           goto finished;
  1765.         }
  1766.     }
  1767.   else
  1768.     input_text_offset += strlen ("@setfilename");
  1769.  
  1770.   real_output_filename = (char *)NULL;
  1771.  
  1772.   if (!command_output_filename)
  1773.     get_until ("\n", &output_filename);
  1774.   else
  1775.     {
  1776.       discard_until ("\n");
  1777.       real_output_filename = output_filename = command_output_filename;
  1778.       command_output_filename = (char *)NULL;
  1779.     }
  1780.  
  1781.   canon_white (output_filename);
  1782.  
  1783.   if (amiga_guide) {
  1784.     if (no_headers)
  1785.       printf ("Making ASCII file `%s' from `%s'.\n", output_filename, name);
  1786.     else
  1787.       printf ("Making AmigaGuide® file `%s' from `%s'.\n", output_filename, name);
  1788.   }
  1789.   else
  1790.     printf ("Making info file `%s' from `%s'.\n", output_filename, name);
  1791.  
  1792.   if (verbose_mode)
  1793.     fprintf (stderr, "  The input file contains %d characters.\n",
  1794.              size_of_input_text);
  1795.  
  1796.   if (real_output_filename &&
  1797.       strcmp (real_output_filename, "-") == 0)
  1798.     {
  1799.       output_stream = stdout;
  1800.     }
  1801.   else
  1802.     {
  1803.       if (!real_output_filename)
  1804.         real_output_filename = expand_filename (output_filename, name);
  1805.  
  1806.       output_stream = fopen (real_output_filename, "w");
  1807.     }
  1808.  
  1809.   if (output_stream == NULL)
  1810.     {
  1811.       fs_error (real_output_filename);
  1812.       goto finished;
  1813.     }
  1814.  
  1815.   /* Make the displayable filename from output_filename.  Only the base
  1816.      portion of the filename need be displayed. */
  1817.   pretty_output_filename = filename_part (output_filename);
  1818.  
  1819.   /* For this file only, count the number of newlines from the top of
  1820.      the file to here.  This way, we keep track of line numbers for
  1821.      error reporting.  Line_number starts at 1, since the user isn't
  1822.      zero-based. */
  1823.   {
  1824.     int temp = 0;
  1825.     line_number = 1;
  1826.     while (temp != input_text_offset)
  1827.       if (input_text[temp++] == '\n')
  1828.         line_number++;
  1829.   }
  1830.  
  1831.   if (!no_headers)
  1832.     {
  1833.       if (amiga_guide)
  1834.         {
  1835.           int old_paragraph_indentation = inhibit_paragraph_indentation;
  1836.           int old_filling_enabled = filling_enabled;
  1837.  
  1838.           inhibit_paragraph_indentation=1;
  1839.           filling_enabled = 0;
  1840.  
  1841.           add_word_args ("@database %s\n",output_filename);
  1842.           add_word_args ("\n@Master %s\n", input_filename);
  1843.           add_word_args ("\n@Width %d\n",fill_column);
  1844.  
  1845.           add_word_args ("\n\nThis is the AmigaGuide® file %s, produced by Makeinfo-%d.%d from ",
  1846.                      output_filename, major_version, minor_version);
  1847.           add_word_args ("\nthe input file %s.\n\n", input_filename);
  1848.  
  1849.           inhibit_paragraph_indentation = old_paragraph_indentation;
  1850.           filling_enabled = old_filling_enabled;
  1851.         }
  1852.       else
  1853.         {
  1854.           add_word_args ("This is Info file %s, produced by Makeinfo-%d.%d from ",
  1855.                       output_filename, major_version, minor_version);
  1856.           add_word_args ("the input file %s.\n", input_filename);
  1857.         }
  1858.       close_paragraph ();
  1859.     }
  1860.  
  1861.   reader_loop ();
  1862.  
  1863. finished:
  1864.   close_paragraph ();
  1865.   flush_file_stack ();
  1866.   if (output_stream != NULL)
  1867.     {
  1868.       output_pending_notes ();
  1869.       free_pending_notes ();
  1870.       if (tag_table != NULL)
  1871.         {
  1872.           tag_table = (TAG_ENTRY *) reverse_list (tag_table);
  1873.           if (!no_headers && !amiga_guide)
  1874.             write_tag_table ();
  1875.         }
  1876.  
  1877.       if (amiga_guide && !no_headers)
  1878.         {
  1879.           inhibit_paragraph_indentation = 1;
  1880.           add_word("\n\n@EndNode\n\n");
  1881.         }
  1882.  
  1883.       if (output_stream != stdout)
  1884.         fclose (output_stream);
  1885.  
  1886.       /* If validating, then validate the entire file right now. */
  1887.       if (validating)
  1888.         validate_file (real_output_filename, tag_table);
  1889.  
  1890.       /* This used to test  && !errors_printed.
  1891.          But some files might have legit warnings.  So split anyway.  */
  1892.       if (splitting)
  1893.         split_file (real_output_filename, 0);
  1894.     }
  1895. }
  1896.  
  1897. free_and_clear (pointer)
  1898.      char **pointer;
  1899. {
  1900.   if ((*pointer) != (char *) NULL)
  1901.     {
  1902.       free (*pointer);
  1903.       *pointer = (char *) NULL;
  1904.     }
  1905. }
  1906.  
  1907.  /* Initialize some state. */
  1908. init_internals ()
  1909. {
  1910.   free_and_clear (¤t_node);
  1911.   free_and_clear (&output_filename);
  1912.   free_and_clear (&command);
  1913.   free_and_clear (&input_filename);
  1914.   free_node_references ();
  1915.   init_insertion_stack ();
  1916.   init_brace_stack ();
  1917.   command_index = 0;
  1918.   in_menu = 0;
  1919. }
  1920.  
  1921. init_paragraph ()
  1922. {
  1923.   free_and_clear (&output_paragraph);
  1924.   output_paragraph = (unsigned char *)xmalloc (paragraph_buffer_len);
  1925.   output_position = 0;
  1926.   output_paragraph[0] = '\0';
  1927.   output_paragraph_offset = 0;
  1928.   output_column = 0;
  1929.   paragraph_is_open = 0;
  1930.   current_indent = 0;
  1931. }
  1932.  
  1933. /* Okay, we are ready to start the conversion.  Call the reader on
  1934.    some text, and fill the text as it is output.  Handle commands by
  1935.    remembering things like open braces and the current file position on a
  1936.    stack, and when the corresponding close brace is found, you can call
  1937.    the function with the proper arguments. */
  1938. reader_loop ()
  1939. {
  1940.   int character;
  1941.   int done = 0;
  1942.   int dash_count = 0;
  1943.   int t=0;
  1944.  
  1945.   while (!done)
  1946.     {
  1947.       if (input_text_offset >= size_of_input_text)
  1948.         {
  1949.           if (filestack)
  1950.             {
  1951.               free (input_filename);
  1952.               free (input_text);
  1953.               popfile ();
  1954.             }
  1955.           else
  1956.             break;
  1957.         }
  1958.  
  1959.       character = curchar ();
  1960.  
  1961.       if (!in_fixed_width_font &&
  1962.           (character == '\'' || character == '`') &&
  1963.           input_text[input_text_offset + 1] == character)
  1964.         {
  1965.           input_text_offset++;
  1966.           character = '"';
  1967.         }
  1968.  
  1969.       if (character == '-')
  1970.         {
  1971.           dash_count++;
  1972.           if (dash_count == 3 && !in_fixed_width_font)
  1973.             {
  1974.               input_text_offset++;
  1975.               continue;
  1976.             }
  1977.         }
  1978.       else
  1979.         {
  1980.           dash_count = 0;
  1981.         }
  1982.  
  1983.       /* If this is a whitespace character, then check to see if the line
  1984.          is blank.  If so, advance to the carriage return. */
  1985.       if (whitespace (character))
  1986.         {
  1987.           register int i = input_text_offset + 1;
  1988.  
  1989.           while (i < size_of_input_text && whitespace (input_text[i]))
  1990.             i++;
  1991.  
  1992.           if (i == size_of_input_text || input_text[i] == '\n')
  1993.             {
  1994.               if (i == size_of_input_text)
  1995.                 i--;
  1996.  
  1997.               input_text_offset = i;
  1998.               character = curchar ();
  1999.             }
  2000.         }
  2001.  
  2002.       if (character == '\n')
  2003.         {
  2004.           line_number++;
  2005.           if (in_menu && input_text_offset + 1 < size_of_input_text)
  2006.             {
  2007.               char *glean_node_from_menu (), *tem;
  2008.               int amiga_guide_convert_menu ();
  2009.  
  2010.               /* Note that the value of TEM is discarded, since it is
  2011.                  gauranteed to be NULL when glean_node_from_menu () is
  2012.                  called with a non-zero argument. */
  2013.               tem = glean_node_from_menu (1);
  2014.  
  2015.               if (amiga_guide && !no_headers)
  2016.                 {
  2017.                   if (amiga_guide_convert_menu())
  2018.                     continue;
  2019.                 }
  2020.             }
  2021.         }
  2022.  
  2023.       switch (character)
  2024.         {
  2025.         case COMMAND_PREFIX:
  2026.           read_command ();
  2027.           if (strcmp (command, "bye") == 0)
  2028.             {
  2029.               done = 1;
  2030.               continue;
  2031.             }
  2032.           break;
  2033.  
  2034.         case '{':
  2035.  
  2036.           /* Special case.  I'm not supposed to see this character by itself.
  2037.              If I do, it means there is a syntax error in the input text.
  2038.              Report the error here, but remember this brace on the stack so
  2039.              you can ignore its partner. */
  2040.  
  2041.           line_error ("Misplaced `{'");
  2042.             remember_brace (misplaced_brace);
  2043.  
  2044.           /* Don't advance input_text_offset since this happens in
  2045.              remember_brace ().*/
  2046.           break;
  2047.  
  2048.         case '}':
  2049.           pop_and_call_brace ();
  2050.           input_text_offset++;
  2051.           break;
  2052.  
  2053.         default:
  2054.           add_char (character);
  2055.           input_text_offset++;
  2056.         }
  2057.  
  2058.     }
  2059. }
  2060.  
  2061. /* Find the command corresponding to STRING.  If the command
  2062.    is found, return a pointer to the data structure.  Otherwise
  2063.    return (-1). */
  2064. COMMAND *
  2065. get_command_entry (string)
  2066.      char *string;
  2067. {
  2068.   register int i;
  2069.  
  2070.   for (i = 0; CommandTable[i].name; i++)
  2071.     if (strcmp (CommandTable[i].name, string) == 0)
  2072.       return (&CommandTable[i]);
  2073.  
  2074.   /* This command is not in our predefined command table.  Perhaps
  2075.      it is a user defined command. */
  2076.   for (i = 0; i < user_command_array_len; i++)
  2077.     if (user_command_array[i] &&
  2078.         (strcmp (user_command_array[i]->name, string) == 0))
  2079.       return (user_command_array[i]);
  2080.  
  2081.   /* Nope, we never heard of this command. */
  2082.   return ((COMMAND *) -1);
  2083. }
  2084.  
  2085. /* input_text_offset is right at the command prefix character.
  2086.    Read the next token to determine what to do. */
  2087. read_command ()
  2088. {
  2089.   COMMAND *entry;
  2090.   input_text_offset++;
  2091.   free_and_clear (&command);
  2092.   command = read_token ();
  2093.  
  2094. #if defined (HAVE_MACROS)
  2095.   /* Check to see if this command is a macro.  If so, execute it here. */
  2096.   {
  2097.     MACRO_DEF *def;
  2098.  
  2099.     def = find_macro (command);
  2100.  
  2101.     if (def)
  2102.       {
  2103.         execute_macro (def);
  2104.         return;
  2105.       }
  2106.   }
  2107. #endif /* HAVE_MACROS */
  2108.  
  2109.   entry = get_command_entry (command);
  2110.  
  2111.   if ((int) entry == -1)
  2112.     {
  2113.       line_error ("Unknown info command `%s'", command);
  2114.       return;
  2115.     }
  2116.  
  2117.   if (entry->argument_in_braces)
  2118.     remember_brace (entry->proc);
  2119.  
  2120.   (*(entry->proc)) (START);
  2121. }
  2122.  
  2123. /* Return the string which invokes PROC; a pointer to a function. */
  2124. char *
  2125. find_proc_name (proc)
  2126.      FUNCTION *proc;
  2127. {
  2128.   register int i;
  2129.  
  2130.   for (i = 0; CommandTable[i].name; i++)
  2131.     if (proc == CommandTable[i].proc)
  2132.       return (CommandTable[i].name);
  2133.   return ("NO_NAME!");
  2134. }
  2135.  
  2136. init_brace_stack ()
  2137. {
  2138.   brace_stack = (BRACE_ELEMENT *) NULL;
  2139. }
  2140.  
  2141. remember_brace (proc)
  2142.      FUNCTION *proc;
  2143. {
  2144.   if (curchar () != '{')
  2145.     line_error ("@%s expected `{..}'", command);
  2146.   else
  2147.     input_text_offset++;
  2148.   remember_brace_1 (proc, output_paragraph_offset);
  2149. }
  2150.  
  2151. /* Remember the current output position here.  Save PROC
  2152.    along with it so you can call it later. */
  2153. remember_brace_1 (proc, position)
  2154.      FUNCTION *proc;
  2155.      int position;
  2156. {
  2157.   BRACE_ELEMENT *new = (BRACE_ELEMENT *) xmalloc (sizeof (BRACE_ELEMENT));
  2158.   new->next = brace_stack;
  2159.   new->proc = proc;
  2160.   new->pos = position;
  2161.   new->line = line_number;
  2162.   brace_stack = new;
  2163. }
  2164.  
  2165. /* Pop the top of the brace stack, and call the associated function
  2166.    with the args END and POS. */
  2167. pop_and_call_brace ()
  2168. {
  2169.   BRACE_ELEMENT *temp;
  2170.   FUNCTION *proc;
  2171.   int pos;
  2172.  
  2173.  if (brace_stack == (BRACE_ELEMENT *) NULL)
  2174.          return (line_error ("Unmatched close brace"));
  2175.   pos = brace_stack->pos;
  2176.   proc = brace_stack->proc;
  2177.   temp = brace_stack->next;
  2178.   free (brace_stack);
  2179.   brace_stack = temp;
  2180.  
  2181.   return ((*proc) (END, pos, output_paragraph_offset));
  2182. }
  2183.  
  2184. /* You call discard_braces () when you shouldn't have any braces on the stack.
  2185.    I used to think that this happens for commands that don't take arguments
  2186.    in braces, but that was wrong because of things like @code{foo @@}.  So now
  2187.    I only detect it at the beginning of nodes. */
  2188. discard_braces ()
  2189. {
  2190.   int temp_line_number = line_number;
  2191.   char *proc_name;
  2192.  
  2193.   if (!brace_stack)
  2194.     return;
  2195.  
  2196.   while (brace_stack)
  2197.     {
  2198.       line_number = brace_stack->line;
  2199.       proc_name = find_proc_name (brace_stack->proc);
  2200.       line_error ("@%s missing close brace", proc_name);
  2201.       line_number = temp_line_number;
  2202.       pop_and_call_brace ();
  2203.     }
  2204. }
  2205.  
  2206. get_char_len (character)
  2207.      int character;
  2208. {
  2209.   /* Return the printed length of the character. */
  2210.   int len;
  2211.  
  2212.   switch (character)
  2213.     {
  2214.     case '\t':
  2215.       len = (output_column + 8) & 0xf7;
  2216.       if (len > fill_column)
  2217.         len = fill_column - output_column;
  2218.       else
  2219.         len = len - output_column;
  2220.       break;
  2221.  
  2222.     case '\n':
  2223.       len = fill_column - output_column;
  2224.       break;
  2225.  
  2226.     default:
  2227.       if (character < ' ')
  2228.         len = 2;
  2229.       else
  2230.         len = 1;
  2231.     }
  2232.   return (len);
  2233. }
  2234.  
  2235.  
  2236. add_word_args (format, arg1, arg2, arg3, arg4, arg5)
  2237.      char *format;
  2238. {
  2239.   char buffer[1000];
  2240.   sprintf (buffer, format, arg1, arg2, arg3, arg4, arg5);
  2241.   add_word (buffer);
  2242. }
  2243.  
  2244. /* Add STRING to output_paragraph. */
  2245. add_word (string)
  2246.      char *string;
  2247. {
  2248.   while (*string)
  2249.     add_char (*string++);
  2250. }
  2251.  
  2252. /* Non-zero if the last character inserted has the syntax class of NEWLINE. */
  2253. int last_char_was_newline = 1;
  2254.  
  2255. /* The actual last inserted character.  Note that this may be something
  2256.    other than NEWLINE even if last_char_was_newline is 1. */
  2257. int last_inserted_character = 0;
  2258.  
  2259. /* Non-zero means that a newline character has already been
  2260.    inserted, so close_paragraph () should insert one less. */
  2261. int line_already_broken = 0;
  2262.  
  2263. /* AmigaGuide --- Set to the number of hidden chars in the last @Node */
  2264. int amiga_guide_hidden_chars = 0;
  2265.  
  2266. /* When non-zero we have finished an insertion (see end_insertion ()) and we
  2267.    want to ignore false continued paragraph closings. */
  2268. int insertion_paragraph_closed = 0;
  2269.  
  2270. /* Add the character to the current paragraph.  If filling_enabled is
  2271.    non-zero, then do filling as well. */
  2272. add_char (character)
  2273.      int character;
  2274. {
  2275.   /* If we are avoiding outputting headers, and we are currently
  2276.      in a menu, then simply return. */
  2277.   if (no_headers && in_menu)
  2278.     return;
  2279.  
  2280.   /* If we are adding a character now, then we don't have to
  2281.      ignore close_paragraph () calls any more. */
  2282.   if (must_start_paragraph && character != '\n')
  2283.     {
  2284.       must_start_paragraph = 0;
  2285.       line_already_broken = 0;  /* The line is no longer broken. */
  2286.       if (current_indent > output_column && (!amiga_guide || !in_amiga_guide_button))
  2287.         {
  2288.           indent (current_indent - output_column);
  2289.           output_column = current_indent;
  2290.         }
  2291.     }
  2292.  
  2293.   if (non_splitting_words && member (character, " \t\n"))
  2294.     character = ' ' | 0x80;
  2295.  
  2296.   insertion_paragraph_closed = 0;
  2297.  
  2298.   switch (character)
  2299.     {
  2300.     case '\n':
  2301.       if (!filling_enabled)
  2302.         {
  2303.           insert ('\n');
  2304.  
  2305.           if (force_flush_right)
  2306.             {
  2307.               close_paragraph ();
  2308.               /* Hack to force single blank lines out in this mode. */
  2309.               flush_output ();
  2310.             }
  2311.  
  2312.           output_column = 0;
  2313.  
  2314.           if (!no_indent && paragraph_is_open && (!amiga_guide || !in_amiga_guide_button))
  2315.             indent (output_column = current_indent);
  2316.           break;
  2317.         }
  2318.       else /* CHARACTER is newline, and filling is enabled. */
  2319.         {
  2320. #if defined (NOTDEF)
  2321.           if (sentence_ender (last_inserted_character))
  2322.             {
  2323.               insert (' ');
  2324.               output_column++;
  2325.               last_inserted_character = character;
  2326.             }
  2327. #endif /* NOTDEF */
  2328.         }
  2329.  
  2330.       if (last_char_was_newline)
  2331.         {
  2332.           close_paragraph ();
  2333.           pending_indent = 0;
  2334.         }
  2335.       else
  2336.         {
  2337.           last_char_was_newline = 1;
  2338.           insert (' ');
  2339.           output_column++;
  2340.         }
  2341.       break;
  2342.  
  2343.     default:
  2344.       {
  2345.         int len = get_char_len (character);
  2346.  
  2347.         if ((character == ' ') && (last_char_was_newline))
  2348.           {
  2349.             if (!paragraph_is_open)
  2350.               {
  2351.                 pending_indent++;
  2352.                 return;
  2353.               }
  2354.           }
  2355.  
  2356.         if (!paragraph_is_open)
  2357.           {
  2358.             start_paragraph ();
  2359.  
  2360.             /* If the paragraph is supposed to be indented a certain way,
  2361.                then discard all of the pending whitespace.  Otherwise, we
  2362.                let the whitespace stay. */
  2363.             if (!paragraph_start_indent && (!amiga_guide || !in_amiga_guide_button))
  2364.               indent (pending_indent);
  2365.             pending_indent = 0;
  2366.           }
  2367.  
  2368.         if ((output_column += len) > fill_column)
  2369.           {
  2370.             if (filling_enabled)
  2371.               {
  2372.                 int temp = output_paragraph_offset;
  2373.                 while (--temp > 0 && output_paragraph[temp] != '\n')
  2374.                   {
  2375.                     /* If we have found a space, we have the place to break
  2376.                        the line. */
  2377.                     if (output_paragraph[temp] == ' ')
  2378.                       {
  2379.                         output_paragraph[temp++] = '\n';
  2380.  
  2381.                         /* We have correctly broken the line where we want
  2382.                            to.  What we don't want is spaces following where
  2383.                            we have decided to break the line.  We get rid of
  2384.                            them. */
  2385.                         {
  2386.                           int t1 = temp;
  2387.                           while (t1 < output_paragraph_offset
  2388.                                  && whitespace (output_paragraph[t1]))
  2389.                             t1++;
  2390.  
  2391.                           if (t1 != temp)
  2392.                             {
  2393.                               strncpy ((char *) &output_paragraph[temp],
  2394.                                        (char *) &output_paragraph[t1],
  2395.                                        (output_paragraph_offset - t1));
  2396.                               output_paragraph_offset -= (t1 - temp);
  2397.                             }
  2398.                         }
  2399.  
  2400.                         /* Filled, but now indent if that is right. */
  2401.                         if (indented_fill && current_indent)
  2402.                           {
  2403.                             int buffer_len = ((output_paragraph_offset - temp)
  2404.                                               + current_indent);
  2405.                             char *temp_buffer = (char *)xmalloc (buffer_len);
  2406.                             int indentation = 0;
  2407.  
  2408.                             /* We have to shift any markers that are in
  2409.                                front of the wrap point. */
  2410.                             {
  2411.                               register BRACE_ELEMENT *stack = brace_stack;
  2412.  
  2413.                               while (stack)
  2414.                                 {
  2415.                                   if (stack->pos > temp)
  2416.                                     stack->pos += current_indent;
  2417.                                   stack = stack->next;
  2418.                                 }
  2419.                             }
  2420.  
  2421.                             while (current_indent > 0 &&
  2422.                                    indentation != current_indent)
  2423.                               temp_buffer[indentation++] = ' ';
  2424.  
  2425.                             strncpy ((char *) &temp_buffer[current_indent],
  2426.                                      (char *) &output_paragraph[temp],
  2427.                                      buffer_len - current_indent);
  2428.  
  2429.                             if (output_paragraph_offset + buffer_len
  2430.                                 >= paragraph_buffer_len)
  2431.                               {
  2432.                                 unsigned char *tt = xrealloc
  2433.                                   (output_paragraph,
  2434.                                    (paragraph_buffer_len += buffer_len));
  2435.                                 output_paragraph = tt;
  2436.                               }
  2437.                             strncpy ((char *) &output_paragraph[temp],
  2438.                                      temp_buffer, buffer_len);
  2439.                             output_paragraph_offset += current_indent;
  2440.                             free (temp_buffer);
  2441.                           }
  2442.                         output_column = 0;
  2443.                         if (amiga_guide && !strncmp("@{\"", &output_paragraph[temp+current_indent], 3))
  2444.                           output_column = -amiga_guide_hidden_chars;
  2445.                         while (temp < output_paragraph_offset)
  2446.                           output_column +=
  2447.                             get_char_len (output_paragraph[temp++]);
  2448.                         output_column += len;
  2449.                         break;
  2450.                       }
  2451.                   }
  2452.               }
  2453.           }
  2454.         insert (character);
  2455.         line_already_broken = 0;
  2456.         last_char_was_newline = 0;
  2457.         last_inserted_character = character;
  2458.       }
  2459.     }
  2460. }
  2461.  
  2462. /* Insert CHARACTER into OUTPUT_PARAGRAPH. */
  2463. insert (character)
  2464.      int character;
  2465. {
  2466.   output_paragraph[output_paragraph_offset++] = character;
  2467.   if (output_paragraph_offset == paragraph_buffer_len)
  2468.     {
  2469.       output_paragraph =
  2470.         xrealloc (output_paragraph, (paragraph_buffer_len += 100));
  2471.     }
  2472. }
  2473.  
  2474. /* Remove upto COUNT characters of whitespace from the
  2475.    the current output line.  If COUNT is less than zero,
  2476.    then remove until none left. */
  2477. kill_self_indent (count)
  2478.      int count;
  2479. {
  2480.   /* Handle infinite case first. */
  2481.   if (count < 0)
  2482.     {
  2483.       output_column = 0;
  2484.       while (output_paragraph_offset)
  2485.         {
  2486.           if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  2487.             output_paragraph_offset--;
  2488.           else
  2489.             break;
  2490.         }
  2491.     }
  2492.   else
  2493.     {
  2494.       while (output_paragraph_offset && count--)
  2495.         if (whitespace (output_paragraph[output_paragraph_offset - 1]))
  2496.           output_paragraph_offset--;
  2497.         else
  2498.           break;
  2499.     }
  2500. }
  2501.  
  2502. /* Non-zero means do not honor calls to flush_output (). */
  2503. static int flushing_ignored = 0;
  2504.  
  2505. /* Prevent calls to flush_output () from having any effect. */
  2506. inhibit_output_flushing ()
  2507. {
  2508.   flushing_ignored++;
  2509. }
  2510.  
  2511. /* Allow calls to flush_output () to write the paragraph data. */
  2512. uninhibit_output_flushing ()
  2513. {
  2514.   flushing_ignored--;
  2515. }
  2516.  
  2517. flush_output ()
  2518. {
  2519.   register int i;
  2520.  
  2521.   if (!output_paragraph_offset || flushing_ignored)
  2522.     return;
  2523.  
  2524.   for (i = 0; i < output_paragraph_offset; i++)
  2525.     {
  2526.         if (amiga_guide) {
  2527.         if (output_paragraph[i] == (unsigned char)(' ' | 0x80) ||
  2528.             output_paragraph[i] == (unsigned char)('\t' | 0x80) ||
  2529.             output_paragraph[i] == (unsigned char)('\n' | 0x80))
  2530.           output_paragraph[i] &= 0x7f;
  2531.         if (sentence_ender (UNMETA (output_paragraph[i]) + 32))
  2532.           output_paragraph[i] = (output_paragraph[i] & 0x7f) + 32;
  2533.         }
  2534.         else {
  2535.         if (output_paragraph[i] == (unsigned char)(' ' | 0x80) ||
  2536.             output_paragraph[i] == (unsigned char)('\t' | 0x80) ||
  2537.             output_paragraph[i] == (unsigned char)('\n' | 0x80) ||
  2538.             sentence_ender (UNMETA (output_paragraph[i])))
  2539.           output_paragraph[i] &= 0x7f;
  2540.        }
  2541.     }
  2542.  
  2543.   fwrite (output_paragraph, 1, output_paragraph_offset, output_stream);
  2544.  
  2545.   output_position += output_paragraph_offset;
  2546.   output_paragraph_offset = 0;
  2547. }
  2548.  
  2549. /* How to close a paragraph controlling the number of lines between
  2550.    this one and the last one. */
  2551.  
  2552. /* Paragraph spacing is controlled by this variable.  It is the number of
  2553.    blank lines that you wish to appear between paragraphs.  A value of
  2554.    1 creates a single blank line between paragraphs. */
  2555. int paragraph_spacing = DEFAULT_PARAGRAPH_SPACING;
  2556.  
  2557. /* Close the current paragraph, leaving no blank lines between them. */
  2558. close_single_paragraph ()
  2559. {
  2560.   close_paragraph_with_lines (0);
  2561. }
  2562.  
  2563. /* Close a paragraph after an insertion has ended. */
  2564. close_insertion_paragraph ()
  2565. {
  2566.   if (!insertion_paragraph_closed)
  2567.     {
  2568.       /* Close the current paragraph, breaking the line. */
  2569.       close_single_paragraph ();
  2570.  
  2571.       /* Start a new paragraph here, inserting whatever indention is correct
  2572.          for the now current insertion level (one above the one that we are
  2573.          ending). */
  2574.       start_paragraph ();
  2575.  
  2576.       /* Tell close_paragraph () that the previous line has already been
  2577.          broken, so it should insert one less newline. */
  2578.       line_already_broken = 1;
  2579.  
  2580.       /* Let functions such as add_char () know that we have already found a
  2581.          newline. */
  2582.       ignore_blank_line ();
  2583.     }
  2584.   else
  2585.     {
  2586.       /* If the insertion paragraph is closed already, then we are seeing
  2587.          two `@end' commands in a row.  Note that the first one we saw was
  2588.          handled in the first part of this if-then-else clause, and at that
  2589.          time start_paragraph () was called, partially to handle the proper
  2590.          indentation of the current line.  However, the indentation level
  2591.          may have just changed again, so we may have to outdent the current
  2592.          line to the new indentation level. */
  2593.       if (current_indent < output_column)
  2594.         kill_self_indent (output_column - current_indent);
  2595.     }
  2596.  
  2597.   insertion_paragraph_closed = 1;
  2598. }
  2599.  
  2600. close_paragraph_with_lines (lines)
  2601.      int lines;
  2602. {
  2603.   int old_spacing = paragraph_spacing;
  2604.   paragraph_spacing = lines;
  2605.   close_paragraph ();
  2606.   paragraph_spacing = old_spacing;
  2607. }
  2608.  
  2609. /* Close the currently open paragraph. */
  2610. close_paragraph ()
  2611. {
  2612.   register int i;
  2613.  
  2614.   /* The insertion paragraph is no longer closed. */
  2615.   insertion_paragraph_closed = 0;
  2616.  
  2617.   if (paragraph_is_open && !must_start_paragraph)
  2618.     {
  2619.       register int tindex, c;
  2620.  
  2621.       tindex = output_paragraph_offset;
  2622.  
  2623.       /* Back up to last non-newline/space character, forcing all such
  2624.          subsequent characters to be newlines.  This isn't strictly
  2625.          necessary, but a couple of functions use the presence of a newline
  2626.          to make decisions. */
  2627.       for (tindex = output_paragraph_offset - 1; tindex >= 0; --tindex)
  2628.         {
  2629.           c = output_paragraph[tindex];
  2630.  
  2631.           if (c == ' '|| c == '\n')
  2632.             output_paragraph[tindex] = '\n';
  2633.           else
  2634.             break;
  2635.         }
  2636.  
  2637.       /* All trailing whitespace is ignored. */
  2638.       output_paragraph_offset = ++tindex;
  2639.  
  2640.       /* Break the line if that is appropriate. */
  2641.       if (paragraph_spacing >= 0)
  2642.         insert ('\n');
  2643.  
  2644.       /* Add as many blank lines as is specified in PARAGRAPH_SPACING. */
  2645.       if (!force_flush_right)
  2646.         {
  2647.           for (i = 0; i < (paragraph_spacing - line_already_broken); i++)
  2648.             insert ('\n');
  2649.         }
  2650.  
  2651.       /* If we are doing flush right indentation, then do it now
  2652.          on the paragraph (really a single line). */
  2653.       if (force_flush_right)
  2654.         do_flush_right_indentation ();
  2655.  
  2656.       flush_output ();
  2657.       paragraph_is_open = 0;
  2658.       no_indent = 0;
  2659.       output_column = 0;
  2660.     }
  2661.   ignore_blank_line ();
  2662. }
  2663.  
  2664. /* Make the last line just read look as if it were only a newline. */
  2665. ignore_blank_line ()
  2666. {
  2667.   last_inserted_character = '\n';
  2668.   last_char_was_newline = 1;
  2669. }
  2670.  
  2671. /* Align the end of the text in output_paragraph with fill_column. */
  2672. do_flush_right_indentation ()
  2673. {
  2674.   char *temp;
  2675.   int temp_len;
  2676.  
  2677.   kill_self_indent (-1);
  2678.  
  2679.   if (output_paragraph[0] != '\n')
  2680.     {
  2681.       output_paragraph[output_paragraph_offset] = '\0';
  2682.  
  2683.       if (output_paragraph_offset < fill_column)
  2684.         {
  2685.           register int i;
  2686.  
  2687.           if (fill_column >= paragraph_buffer_len)
  2688.             output_paragraph =
  2689.               xrealloc (output_paragraph,
  2690.                         (paragraph_buffer_len += fill_column));
  2691.  
  2692.           temp_len = strlen ((char *)output_paragraph);
  2693.           temp = (char *)xmalloc (temp_len + 1);
  2694.           bcopy ((char *)output_paragraph, temp, temp_len);
  2695.  
  2696.           for (i = 0; i < fill_column - output_paragraph_offset; i++)
  2697.             output_paragraph[i] = ' ';
  2698.  
  2699.           bcopy (temp, (char *)output_paragraph + i, temp_len);
  2700.           free (temp);
  2701.           output_paragraph_offset = fill_column;
  2702.         }
  2703.     }
  2704. }
  2705.  
  2706. /* Begin a new paragraph. */
  2707. start_paragraph ()
  2708. {
  2709.   /* First close existing one. */
  2710.   if (paragraph_is_open)
  2711.     close_paragraph ();
  2712.  
  2713.   /* In either case, the insertion paragraph is no longer closed. */
  2714.   insertion_paragraph_closed = 0;
  2715.  
  2716.   /* However, the paragraph is open! */
  2717.   paragraph_is_open = 1;
  2718.  
  2719.   /* If we MUST_START_PARAGRAPH, that simply means that start_paragraph ()
  2720.      had to be called before we would allow any other paragraph operations
  2721.      to have an effect. */
  2722.   if (!must_start_paragraph)
  2723.     {
  2724.       int amount_to_indent = 0;
  2725.  
  2726.       /* If doing indentation, then insert the appropriate amount. */
  2727.       if (!no_indent  && (!amiga_guide || !in_amiga_guide_button))
  2728.         {
  2729.           if (inhibit_paragraph_indentation)
  2730.             {
  2731.               amount_to_indent = current_indent;
  2732.               if (inhibit_paragraph_indentation < 0)
  2733.                 inhibit_paragraph_indentation++;
  2734.             }
  2735.           else if (paragraph_start_indent < 0)
  2736.             amount_to_indent = current_indent;
  2737.           else
  2738.             amount_to_indent = current_indent + paragraph_start_indent;
  2739.  
  2740.           if (amount_to_indent >= output_column)
  2741.             {
  2742.               amount_to_indent -= output_column;
  2743.               indent (amount_to_indent);
  2744.               output_column += amount_to_indent;
  2745.             }
  2746.         }
  2747.     }
  2748.   else
  2749.     must_start_paragraph = 0;
  2750. }
  2751.  
  2752. /* Insert the indentation specified by AMOUNT. */
  2753. indent (amount)
  2754.      int amount;
  2755. {
  2756.   while (--amount >= 0)
  2757.     insert (' ');
  2758. }
  2759.  
  2760. /* Search forward for STRING in input_text.
  2761.    FROM says where where to start. */
  2762. search_forward (string, from)
  2763.      char *string;
  2764.      int from;
  2765. {
  2766.   int len = strlen (string);
  2767.  
  2768.   while (from < size_of_input_text)
  2769.     {
  2770.       if (strncmp (input_text + from, string, len) == 0)
  2771.         return (from);
  2772.       from++;
  2773.     }
  2774.   return (-1);
  2775. }
  2776.  
  2777. /* Whoops, Unix doesn't have stricmp. */
  2778.  
  2779. /* Case independent string compare. */
  2780. stricmp (string1, string2)
  2781.      char *string1, *string2;
  2782. {
  2783.   char ch1, ch2;
  2784.  
  2785.   for (;;)
  2786.     {
  2787.       ch1 = *string1++;
  2788.       ch2 = *string2++;
  2789.       if (!(ch1 | ch2))
  2790.         return (0);
  2791.  
  2792.       ch1 = coerce_to_upper (ch1);
  2793.       ch2 = coerce_to_upper (ch2);
  2794.  
  2795.       if (ch1 != ch2)
  2796.         return (ch1-ch2);
  2797.     }
  2798. }
  2799.  
  2800.  
  2801. enum insertion_type { menu, quotation, lisp, smalllisp, example,
  2802.   smallexample, display, itemize, format, enumerate, cartouche, table,
  2803.   ftable, vtable, group, ifinfo, flushleft, flushright, ifset, ifclear, deffn,
  2804.   defun, defmac, defspec, defvr, defvar, defopt, deftypefn,
  2805.   deftypefun, deftypevr, deftypevar, defcv, defivar, defop, defmethod,
  2806.   deftypemethod, deftp, bad_type };
  2807.  
  2808. char *insertion_type_names[] = { "menu", "quotation", "lisp",
  2809.   "smalllisp", "example", "smallexample", "display", "itemize",
  2810.   "format", "enumerate", "cartouche", "table", "ftable", "vtable", "group",
  2811.   "ifinfo", "flushleft", "flushright", "ifset", "ifclear", "deffn",
  2812.   "defun", "defmac", "defspec", "defvr", "defvar", "defopt",
  2813.   "deftypefn", "deftypefun", "deftypevr", "deftypevar", "defcv",
  2814.   "defivar", "defop", "defmethod", "deftypemethod", "deftp",
  2815.   "bad_type" };
  2816.  
  2817. int insertion_level = 0;
  2818. typedef struct istack_elt
  2819. {
  2820.   struct istack_elt *next;
  2821.   char *item_function;
  2822.   int line_number;
  2823.   int filling_enabled;
  2824.   int indented_fill;
  2825.   enum insertion_type insertion;
  2826.   int inhibited;
  2827. } INSERTION_ELT;
  2828.  
  2829. INSERTION_ELT *insertion_stack = (INSERTION_ELT *) NULL;
  2830.  
  2831. init_insertion_stack ()
  2832. {
  2833.   insertion_stack = (INSERTION_ELT *) NULL;
  2834. }
  2835.  
  2836. /* Return the type of the current insertion. */
  2837. enum insertion_type
  2838. current_insertion_type ()
  2839. {
  2840.   if (!insertion_level)
  2841.     return (bad_type);
  2842.   else
  2843.     return (insertion_stack->insertion);
  2844. }
  2845.  
  2846. /* Return a pointer to the string which is the function
  2847.    to wrap around items. */
  2848. char *
  2849. current_item_function ()
  2850. {
  2851.   if (!insertion_level)
  2852.     return ((char *) NULL);
  2853.   else
  2854.     return (insertion_stack->item_function);
  2855. }
  2856.  
  2857. char *
  2858. get_item_function ()
  2859. {
  2860.   char *item_function;
  2861.   get_rest_of_line (&item_function);
  2862.   backup_input_pointer ();
  2863.   canon_white (item_function);
  2864.   return (item_function);
  2865. }
  2866.  
  2867.  /* Push the state of the current insertion on the stack. */
  2868. push_insertion (type, item_function)
  2869.      enum insertion_type type;
  2870.      char *item_function;
  2871. {
  2872.   INSERTION_ELT *new = (INSERTION_ELT *) xmalloc (sizeof (INSERTION_ELT));
  2873.  
  2874.   new->item_function = item_function;
  2875.   new->filling_enabled = filling_enabled;
  2876.   new->indented_fill = indented_fill;
  2877.   new->insertion = type;
  2878.   new->line_number = line_number;
  2879.   new->inhibited = inhibit_paragraph_indentation;
  2880.   new->next = insertion_stack;
  2881.   insertion_stack = new;
  2882.   insertion_level++;
  2883. }
  2884.  
  2885.  /* Pop the value on top of the insertion stack into the
  2886.     global variables. */
  2887. pop_insertion ()
  2888. {
  2889.   INSERTION_ELT *temp = insertion_stack;
  2890.  
  2891.   if (temp == (INSERTION_ELT *) NULL)
  2892.     return;
  2893.  
  2894.   inhibit_paragraph_indentation = temp->inhibited;
  2895.   filling_enabled = temp->filling_enabled;
  2896.   indented_fill = temp->indented_fill;
  2897.   free_and_clear (&(temp->item_function));
  2898.   insertion_stack = insertion_stack->next;
  2899.   free (temp);
  2900.   insertion_level--;
  2901. }
  2902.  
  2903.  /* Return a pointer to the print name of this
  2904.     enumerated type. */
  2905. char *
  2906. insertion_type_pname (type)
  2907.      enum insertion_type type;
  2908. {
  2909.   if ((int) type < (int) bad_type)
  2910.     return (insertion_type_names[(int) type]);
  2911.   else
  2912.     return ("Broken-Type in insertion_type_pname");
  2913. }
  2914.  
  2915. /* Return the insertion_type associated with NAME.
  2916.    If the type is not one of the known ones, return BAD_TYPE. */
  2917. enum insertion_type
  2918. find_type_from_name (name)
  2919.      char *name;
  2920. {
  2921.   int index = 0;
  2922.   while (index < (int) bad_type)
  2923.     {
  2924.       if (stricmp (name, insertion_type_names[index]) == 0)
  2925.         return (enum insertion_type) index;
  2926.       index++;
  2927.     }
  2928.   return (bad_type);
  2929. }
  2930.  
  2931. do_nothing ()
  2932. {
  2933. }
  2934.  
  2935. int
  2936. defun_insertion (type)
  2937.      enum insertion_type type;
  2938. {
  2939.   return
  2940.     ((type == deffn)
  2941.      || (type == defun)
  2942.      || (type == defmac)
  2943.      || (type == defspec)
  2944.      || (type == defvr)
  2945.      || (type == defvar)
  2946.      || (type == defopt)
  2947.      || (type == deftypefn)
  2948.      || (type == deftypefun)
  2949.      || (type == deftypevr)
  2950.      || (type == deftypevar)
  2951.      || (type == defcv)
  2952.      || (type == defivar)
  2953.      || (type == defop)
  2954.      || (type == defmethod)
  2955.      || (type == deftypemethod)
  2956.      || (type == deftp));
  2957. }
  2958.  
  2959. /* MAX_NS is the maximum nesting level for enumerations.  I picked 100
  2960.    which seemed reasonable.  This doesn't control the number of items,
  2961.    just the number of nested lists. */
  2962. #define max_stack_depth 100
  2963. #define ENUM_DIGITS 1
  2964. #define ENUM_ALPHA  2
  2965. typedef struct {
  2966.   int enumtype;
  2967.   int enumval;
  2968. } DIGIT_ALPHA;
  2969.  
  2970. DIGIT_ALPHA enumstack[max_stack_depth];
  2971. int enumstack_offset = 0;
  2972. int current_enumval = 1;
  2973. int current_enumtype = ENUM_DIGITS;
  2974. char *enumeration_arg = (char *)NULL;
  2975.  
  2976. start_enumerating (at, type)
  2977.      int at, type;
  2978. {
  2979.   if ((enumstack_offset + 1) == max_stack_depth)
  2980.     {
  2981.       line_error ("Enumeration stack overflow");
  2982.       return;
  2983.     }
  2984.   enumstack[enumstack_offset].enumtype = current_enumtype;
  2985.   enumstack[enumstack_offset].enumval = current_enumval;
  2986.   enumstack_offset++;
  2987.   current_enumval = at;
  2988.   current_enumtype = type;
  2989. }
  2990.  
  2991. stop_enumerating ()
  2992. {
  2993.   --enumstack_offset;
  2994.   if (enumstack_offset < 0)
  2995.     enumstack_offset = 0;
  2996.  
  2997.   current_enumval = enumstack[enumstack_offset].enumval;
  2998.   current_enumtype = enumstack[enumstack_offset].enumtype;
  2999. }
  3000.  
  3001. /* Place a letter or digits into the output stream. */
  3002. enumerate_item ()
  3003. {
  3004.   char temp[10];
  3005.  
  3006.   if (current_enumtype == ENUM_ALPHA)
  3007.     {
  3008.       if (current_enumval == ('z' + 1) || current_enumval == ('Z' + 1))
  3009.         {
  3010.           current_enumval = ((current_enumval - 1) == 'z' ? 'a' : 'A');
  3011.           warning ("Lettering overflow, restarting at %c", current_enumval);
  3012.         }
  3013.       sprintf (temp, "%c. ", current_enumval);
  3014.     }
  3015.   else
  3016.     sprintf (temp, "%d. ", current_enumval);
  3017.  
  3018.   indent (output_column += (current_indent - strlen (temp)));
  3019.   add_word (temp);
  3020.   current_enumval++;
  3021. }
  3022.  
  3023. /* This is where the work for all the "insertion" style
  3024.    commands is done.  A huge switch statement handles the
  3025.    various setups, and generic code is on both sides. */
  3026. begin_insertion (type)
  3027.      enum insertion_type type;
  3028. {
  3029.   int no_discard = 0;
  3030.  
  3031.   if (defun_insertion (type))
  3032.     {
  3033.       push_insertion (type, savestring (""));
  3034.       no_discard++;
  3035.     }
  3036.   else
  3037.     push_insertion (type, get_item_function ());
  3038.  
  3039.   switch (type)
  3040.     {
  3041.     case menu:
  3042.       if (!no_headers)
  3043.         close_paragraph ();
  3044.  
  3045.       filling_enabled = no_indent = 0;
  3046.       inhibit_paragraph_indentation = 1;
  3047.  
  3048.       if (!no_headers && !amiga_guide)
  3049.          {
  3050.             add_word ("* Menu:\n");
  3051.          }
  3052.  
  3053.       in_menu++;
  3054.       no_discard++;
  3055.       break;
  3056.  
  3057.       /* I think @quotation is meant to do filling.
  3058.          If you don't want filling, then use @example. */
  3059.     case quotation:
  3060.       close_single_paragraph ();
  3061.       last_char_was_newline = no_indent = 0;
  3062.       indented_fill = filling_enabled = 1;
  3063.       inhibit_paragraph_indentation = 1;
  3064.       current_indent += default_indentation_increment;
  3065.       break;
  3066.  
  3067.     case display:
  3068.     case example:
  3069.     case smallexample:
  3070.     case lisp:
  3071.     case smalllisp:
  3072.       /* Just like @example, but no indentation. */
  3073.     case format:
  3074.  
  3075.       close_single_paragraph ();
  3076.       inhibit_paragraph_indentation = 1;
  3077.       in_fixed_width_font++;
  3078.       filling_enabled = 0;
  3079.       last_char_was_newline = 0;
  3080.  
  3081.       if (type != format)
  3082.         current_indent += default_indentation_increment;
  3083.  
  3084.       break;
  3085.  
  3086.     case table:
  3087.     case ftable:
  3088.     case vtable:
  3089.     case itemize:
  3090.       close_single_paragraph ();
  3091.       current_indent += default_indentation_increment;
  3092.       filling_enabled = indented_fill = 1;
  3093. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3094.       inhibit_paragraph_indentation = 0;
  3095. #else
  3096.       inhibit_paragraph_indentation = 1;
  3097. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3098.  
  3099.       /* Make things work for losers who forget the itemize syntax. */
  3100.       if (type == itemize)
  3101.         {
  3102.           if (!(*insertion_stack->item_function))
  3103.             {
  3104.               free (insertion_stack->item_function);
  3105.               insertion_stack->item_function = savestring ("@bullet");
  3106.             }
  3107.         }
  3108.       break;
  3109.  
  3110.     case enumerate:
  3111.       close_single_paragraph ();
  3112.       no_indent = 0;
  3113. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  3114.       inhibit_paragraph_indentation = 0;
  3115. #else
  3116.       inhibit_paragraph_indentation = 1;
  3117. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  3118.  
  3119.       current_indent += default_indentation_increment;
  3120.       filling_enabled = indented_fill = 1;
  3121.  
  3122.       if (isdigit (*enumeration_arg))
  3123.         start_enumerating (atoi (enumeration_arg), ENUM_DIGITS);
  3124.       else
  3125.         start_enumerating (*enumeration_arg, ENUM_ALPHA);
  3126.       break;
  3127.  
  3128.       /* Does nothing special in makeinfo. */
  3129.     case group:
  3130.       /* Only close the paragraph if we are not inside of an @example. */
  3131.       if (!insertion_stack->next ||
  3132.           insertion_stack->next->insertion != example)
  3133.         close_single_paragraph ();
  3134.       break;
  3135.  
  3136.       /* Insertions that are no-ops in info, but do something in TeX. */
  3137.     case ifinfo:
  3138.     case ifset:
  3139.     case ifclear:
  3140.     case cartouche:
  3141.       break;
  3142.  
  3143.     case deffn:
  3144.     case defun:
  3145.     case defmac:
  3146.     case defspec:
  3147.     case defvr:
  3148.     case defvar:
  3149.     case defopt:
  3150.     case deftypefn:
  3151.     case deftypefun:
  3152.     case deftypevr:
  3153.     case deftypevar:
  3154.     case defcv:
  3155.     case defivar:
  3156.     case defop:
  3157.     case defmethod:
  3158.     case deftypemethod:
  3159.     case deftp:
  3160.       inhibit_paragraph_indentation = 1;
  3161.       filling_enabled = indented_fill = 1;
  3162.       current_indent += default_indentation_increment;
  3163.       no_indent = 0;
  3164.       break;
  3165.  
  3166.     case flushleft:
  3167.       close_single_paragraph ();
  3168.       inhibit_paragraph_indentation = 1;
  3169.       filling_enabled = indented_fill = no_indent = 0;
  3170.       break;
  3171.  
  3172.     case flushright:
  3173.       close_single_paragraph ();
  3174.       filling_enabled = indented_fill = no_indent = 0;
  3175.       inhibit_paragraph_indentation = 1;
  3176.       force_flush_right++;
  3177.       break;
  3178.     }
  3179.  
  3180.   if (!no_discard)
  3181.     discard_until ("\n");
  3182. }
  3183.  
  3184. /* Try to end the insertion with the specified TYPE.
  3185.    TYPE, with a value of bad_type,  gets translated to match
  3186.    the value currently on top of the stack.
  3187.    Otherwise, if TYPE doesn't match the top of the insertion stack,
  3188.    give error. */
  3189. end_insertion (type)
  3190.      enum insertion_type type;
  3191. {
  3192.   enum insertion_type temp_type;
  3193.  
  3194.   if (!insertion_level)
  3195.     return;
  3196.  
  3197.   temp_type = current_insertion_type ();
  3198.  
  3199.   if (type == bad_type)
  3200.     type = temp_type;
  3201.  
  3202.   if (type != temp_type)
  3203.     {
  3204.       line_error
  3205.         ("`%cend' expected `%s', but saw `%s'.", COMMAND_PREFIX,
  3206.          insertion_type_pname (temp_type), insertion_type_pname (type));
  3207.       return;
  3208.     }
  3209.  
  3210.   pop_insertion ();
  3211.  
  3212.   switch (type)
  3213.     {
  3214.       /* "Insertions which have no effect on paragraph formatting. */
  3215.     case ifinfo:
  3216.     case ifset:
  3217.     case ifclear:
  3218.       break;
  3219.  
  3220.     case menu:
  3221.       in_menu--;                /* No longer hacking menus. */
  3222.       if (!no_headers)
  3223.         close_insertion_paragraph ();
  3224.       break;
  3225.  
  3226.     case enumerate:
  3227.       stop_enumerating ();
  3228.       close_insertion_paragraph ();
  3229.       current_indent -= default_indentation_increment;
  3230.       break;
  3231.  
  3232.     case flushleft:
  3233.     case group:
  3234.     case cartouche:
  3235.       close_insertion_paragraph ();
  3236.       break;
  3237.  
  3238.     case format:
  3239.     case display:
  3240.     case example:
  3241.     case smallexample:
  3242.     case lisp:
  3243.     case smalllisp:
  3244.     case quotation:
  3245.  
  3246.       /* @quotation is the only one of the above without a fixed width
  3247.          font. */
  3248.       if (type != quotation)
  3249.         in_fixed_width_font--;
  3250.  
  3251.       /* @format is the only fixed_width insertion without a change
  3252.          in indentation. */
  3253.       if (type != format)
  3254.         current_indent -= default_indentation_increment;
  3255.  
  3256.       /* The ending of one of these insertions always marks the
  3257.          start of a new paragraph. */
  3258.       close_insertion_paragraph ();
  3259.       break;
  3260.  
  3261.     case table:
  3262.     case ftable:
  3263.     case vtable:
  3264.     case itemize:
  3265.       current_indent -= default_indentation_increment;
  3266.       break;
  3267.  
  3268.     case flushright:
  3269.       force_flush_right--;
  3270.       close_insertion_paragraph ();
  3271.       break;
  3272.  
  3273.       /* Handle the @defun style insertions with a default clause. */
  3274.     default:
  3275.       current_indent -= default_indentation_increment;
  3276.       close_insertion_paragraph ();
  3277.       break;
  3278.     }
  3279. }
  3280.  
  3281. /* Insertions cannot cross certain boundaries, such as node beginnings.  In
  3282.    code that creates such boundaries, you should call discard_insertions ()
  3283.    before doing anything else.  It prints the errors for you, and cleans up
  3284.    the insertion stack. */
  3285. discard_insertions ()
  3286. {
  3287.   int real_line_number = line_number;
  3288.   while (insertion_stack)
  3289.     {
  3290.       if (insertion_stack->insertion == ifinfo ||
  3291.           insertion_stack->insertion == ifset ||
  3292.           insertion_stack->insertion == ifclear ||
  3293.           insertion_stack->insertion == cartouche)
  3294.         break;
  3295.       else
  3296.         {
  3297.           char *offender = (char *)
  3298.             insertion_type_pname (insertion_stack->insertion);
  3299.  
  3300.           line_number = insertion_stack->line_number;
  3301.           line_error ("This `%s' doesn't have a matching `%cend %s'", offender,
  3302.                       COMMAND_PREFIX, offender);
  3303.           pop_insertion ();
  3304.         }
  3305.     }
  3306.   line_number = real_line_number;
  3307. }
  3308.  
  3309. /* The actual commands themselves. */
  3310.  
  3311. /* Commands which insert themselves. */
  3312. insert_self ()
  3313. {
  3314.   add_word (command);
  3315. }
  3316.  
  3317. /* Force a line break in the output. */
  3318. cm_asterisk ()
  3319. {
  3320.   close_single_paragraph ();
  3321. #if !defined (ASTERISK_NEW_PARAGRAPH)
  3322.   cm_noindent ();
  3323. #endif /* ASTERISK_NEW_PARAGRAPH */
  3324. }
  3325.  
  3326. /* Insert ellipsis. */
  3327. cm_dots (arg)
  3328.      int arg;
  3329. {
  3330.   if (arg == START)
  3331.     add_word ("...");
  3332. }
  3333.  
  3334. cm_bullet (arg)
  3335.      int arg;
  3336. {
  3337.   if (arg == START)
  3338.     add_char ('*');
  3339. }
  3340.  
  3341. cm_minus (arg)
  3342.      int arg;
  3343. {
  3344.   if (arg == START)
  3345.     add_char ('-');
  3346. }
  3347.  
  3348. /* Insert "TeX". */
  3349. cm_TeX (arg)
  3350.      int arg;
  3351. {
  3352.   if (arg == START)
  3353.     add_word ("TeX");
  3354. }
  3355.  
  3356. cm_copyright (arg)
  3357.      int arg;
  3358. {
  3359.   if (arg == START)
  3360.     add_word ("(C)");
  3361. }
  3362.  
  3363. cm_today (arg)
  3364.      int arg;
  3365. {
  3366.   static char * months [12] =
  3367.     { "January", "February", "March", "April", "May", "June", "July",
  3368.         "August", "September", "October", "November", "December" };
  3369.   if (arg == START)
  3370.     {
  3371.       long timer = (time (0));
  3372.       struct tm * ts = (localtime (&timer));
  3373.       add_word_args
  3374.         ("%d %s %d",
  3375.          (ts -> tm_mday),
  3376.          (months [ts -> tm_mon]),
  3377.          ((ts -> tm_year) + 1900));
  3378.     }
  3379. }
  3380.  
  3381. cm_code (arg)
  3382.      int arg;
  3383. {
  3384.   extern int printing_index;
  3385.  
  3386.   if (printing_index)
  3387.     return;
  3388.  
  3389.   if (arg == START)
  3390.     {
  3391.       in_fixed_width_font++;
  3392.       add_char ('`');
  3393.     }
  3394.   else
  3395.     {
  3396.       add_word ("'");
  3397.       in_fixed_width_font--;
  3398.     }
  3399. }
  3400.  
  3401. cm_samp (arg)
  3402.      int arg;
  3403. {
  3404.   cm_code (arg);
  3405. }
  3406.  
  3407. cm_file (arg)
  3408.      int arg;
  3409. {
  3410.   cm_code (arg);
  3411. }
  3412.  
  3413. cm_kbd (arg)
  3414.      int arg;
  3415. {
  3416.   cm_code (arg);
  3417. }
  3418.  
  3419. cm_key (arg)
  3420.      int arg;
  3421. {
  3422. }
  3423.  
  3424. /* Convert the character at position into CTL. */
  3425. cm_ctrl (arg, position)
  3426.      int arg, position;
  3427. {
  3428.   if (arg == END)
  3429.     output_paragraph[position - 1] = CTL (output_paragraph[position]);
  3430. }
  3431.  
  3432. /* Small Caps in makeinfo just does all caps. */
  3433. cm_sc (arg, start_pos, end_pos)
  3434.      int arg, start_pos, end_pos;
  3435. {
  3436.   if (arg == END)
  3437.     {
  3438.       while (start_pos < end_pos)
  3439.         {
  3440.           output_paragraph[start_pos] =
  3441.             coerce_to_upper (output_paragraph[start_pos]);
  3442.           start_pos++;
  3443.         }
  3444.     }
  3445. }
  3446.  
  3447. /* @var in makeinfo just uppercases the text. */
  3448. cm_var (arg, start_pos, end_pos)
  3449.      int arg, start_pos, end_pos;
  3450. {
  3451.   if (arg == END)
  3452.     {
  3453.       while (start_pos < end_pos)
  3454.         {
  3455.           output_paragraph[start_pos] =
  3456.             coerce_to_upper (output_paragraph[start_pos]);
  3457.           start_pos++;
  3458.         }
  3459.     }
  3460. }
  3461.  
  3462. cm_dfn (arg, position)
  3463.      int arg, position;
  3464. {
  3465.   add_char ('"');
  3466. }
  3467.  
  3468. cm_emph (arg)
  3469.      int arg;
  3470. {
  3471.   add_char ('*');
  3472. }
  3473.  
  3474. cm_strong (arg, position)
  3475.      int arg, position;
  3476. {
  3477.   cm_emph (arg);
  3478. }
  3479.  
  3480. cm_cite (arg, position)
  3481.      int arg, position;
  3482. {
  3483.   if (arg == START)
  3484.     add_word ("`");
  3485.   else
  3486.     add_word ("'");
  3487. }
  3488.  
  3489. /* Current text is italicized. */
  3490. cm_italic (arg, start, end)
  3491.      int arg, start, end;
  3492. {
  3493. }
  3494.  
  3495. /* Current text is highlighted. */
  3496. cm_bold (arg, start, end)
  3497.      int arg, start, end;
  3498. {
  3499.   cm_italic (arg);
  3500. }
  3501.  
  3502. /* Current text is in roman font. */
  3503. cm_roman (arg, start, end)
  3504.      int arg, start, end;
  3505. {
  3506. }
  3507.  
  3508. /* Current text is in roman font. */
  3509. cm_titlefont (arg, start, end)
  3510.      int arg, start, end;
  3511. {
  3512. }
  3513.  
  3514. /* Italicize titles. */
  3515. cm_title (arg, start, end)
  3516.      int arg, start, end;
  3517. {
  3518.   cm_italic (arg);
  3519. }
  3520.  
  3521. /* @refill is a NOP. */
  3522. cm_refill ()
  3523. {
  3524. }
  3525.  
  3526. /* Prevent the argument from being split across two lines. */
  3527. cm_w (arg, start, end)
  3528.      int arg, start, end;
  3529. {
  3530.   if (arg == START)
  3531.     non_splitting_words++;
  3532.   else
  3533.     non_splitting_words--;
  3534. }
  3535.  
  3536.  
  3537. /* Explain that this command is obsolete, thus the user shouldn't
  3538.    do anything with it. */
  3539. cm_obsolete (arg, start, end)
  3540.      int arg, start, end;
  3541. {
  3542.   if (arg == START)
  3543.     warning ("The command `@%s' is obsolete", command);
  3544. }
  3545.  
  3546. /* Insert the text following input_text_offset up to the end of the line
  3547.    in a new, separate paragraph.  Directly underneath it, insert a
  3548.    line of WITH_CHAR, the same length of the inserted text. */
  3549. insert_and_underscore (with_char)
  3550.      int with_char;
  3551. {
  3552.   int len, i, old_no_indent;
  3553.   char *temp;
  3554.  
  3555.   close_paragraph ();
  3556.   filling_enabled =  indented_fill = 0;
  3557.   old_no_indent = no_indent;
  3558.   no_indent = 1;
  3559.   get_rest_of_line (&temp);
  3560.  
  3561.   len = output_position;
  3562.   execute_string ("%s\n", temp);
  3563.   free (temp);
  3564.  
  3565.   len = ((output_position + output_paragraph_offset) - 1) - len;
  3566.   for (i = 0; i < len; i++)
  3567.     add_char (with_char);
  3568.   insert ('\n');
  3569.   close_paragraph ();
  3570.   filling_enabled = 1;
  3571.   no_indent = old_no_indent;
  3572. }
  3573.  
  3574. /* The remainder of the text on this line is a chapter heading. */
  3575. cm_chapter ()
  3576. {
  3577.   insert_and_underscore ('*');
  3578. }
  3579.  
  3580. /* The remainder of the text on this line is a section heading. */
  3581. cm_section ()
  3582. {
  3583.   insert_and_underscore ('=');
  3584. }
  3585.  
  3586. /* The remainder of the text on this line is a subsection heading. */
  3587. cm_subsection ()
  3588. {
  3589.   insert_and_underscore ('-');
  3590. }
  3591.  
  3592. /* The remainder of the text on this line is a subsubsection heading. */
  3593. cm_subsubsection ()
  3594. {
  3595.   insert_and_underscore ('.');
  3596. }
  3597.  
  3598. /* Here is a structure which associates sectioning commands with
  3599.    an integer, hopefully to reflect the `depth' of the current
  3600.    section. */
  3601. struct {
  3602.   char *name;
  3603.   int level;
  3604. } section_alist[] = {
  3605.   { "unnumberedsubsubsec", 5 },
  3606.   { "unnumberedsubsec", 4 },
  3607.   { "unnumberedsec", 3 },
  3608.   { "unnumbered", 2 },
  3609.   { "appendixsubsubsec", 5 },
  3610.   { "appendixsubsec", 4 },
  3611.   { "appendixsec", 3 },
  3612.   { "appendixsection", 3 },
  3613.   { "appendix", 2 },
  3614.   { "subsubsec", 5 },
  3615.   { "subsubsection", 5 },
  3616.   { "subsection", 4 },
  3617.   { "section", 3 },
  3618.   { "chapter", 2 },
  3619.   { "top", 1 },
  3620.  
  3621.   { (char *)NULL, 0 }
  3622. };
  3623.  
  3624. /* Return an integer which identifies the type section present in TEXT. */
  3625. int
  3626. what_section (text)
  3627.      char *text;
  3628. {
  3629.   register int i, j;
  3630.   char *t;
  3631.  
  3632.  find_section_command:
  3633.   for (j = 0; text[j] && cr_or_whitespace (text[j]); j++);
  3634.   if (text[j] != '@')
  3635.     return (-1);
  3636.  
  3637.   text = text + j + 1;
  3638.  
  3639.   /* We skip @comment commands. */
  3640.   if ((strncmp (text, "comment", strlen ("comment")) == 0) ||
  3641.       (strncmp (text, "c ", strlen ("c ")) == 0))
  3642.     {
  3643.       while (*text++ != '\n');
  3644.       goto find_section_command;
  3645.     }
  3646.  
  3647.   /* Handle italicized sectioning commands. */
  3648.   if (*text == 'i')
  3649.     text++;
  3650.  
  3651.   for (j = 0; text[j] && !cr_or_whitespace (text[j]); j++);
  3652.  
  3653.   for (i = 0; t = section_alist[i].name; i++)
  3654.     {
  3655.       if (j == strlen (t) && strncmp (t, text, j) == 0)
  3656.         return (section_alist[i].level);
  3657.     }
  3658.   return (-1);
  3659. }
  3660.  
  3661. /* Treat this just like @unnumbered.  The only difference is
  3662.    in node defaulting. */
  3663. cm_top ()
  3664. {
  3665.   static int top_encountered = 0;
  3666.   cm_unnumbered ();
  3667.  
  3668.   /* It is an error to have more than one @top. */
  3669.   if (top_encountered)
  3670.     {
  3671.       TAG_ENTRY *tag = tag_table;
  3672.  
  3673.       line_error ("There already is a node having @top as a section");
  3674.  
  3675.       while (tag != (TAG_ENTRY *)NULL)
  3676.         {
  3677.           if ((tag->flags & IS_TOP))
  3678.             {
  3679.               int old_line_number = line_number;
  3680.               line_number = tag->line_no;
  3681.               line_error ("Here is the @top node.");
  3682.               line_number = old_line_number;
  3683.               return;
  3684.             }
  3685.           tag = tag->next_ent;
  3686.         }
  3687.     }
  3688.   else
  3689.     {
  3690.       top_encountered = 1;
  3691.  
  3692.       /* The most recently defined node is the top node. */
  3693.       if (tag_table)
  3694.         tag_table->flags |= IS_TOP;
  3695.  
  3696.       /* Now set the logical hierarchical level of the Top node. */
  3697.       {
  3698.         int orig_offset = input_text_offset;
  3699.  
  3700.         input_text_offset = search_forward ("\n@node", orig_offset);
  3701.  
  3702.         if (input_text_offset > 0)
  3703.           {
  3704.             int this_section;
  3705.  
  3706.             /* Move to the end of this line, and find out what the
  3707.                sectioning command is here. */
  3708.             while (input_text[input_text_offset] != '\n')
  3709.               input_text_offset++;
  3710.  
  3711.             if (input_text_offset < size_of_input_text)
  3712.               input_text_offset++;
  3713.  
  3714.             this_section = what_section (input_text + input_text_offset);
  3715.  
  3716.             /* If we found a sectioning command, then give the top section
  3717.                a level of this section - 1. */
  3718.             if (this_section != -1)
  3719.               {
  3720.                 register int i;
  3721.  
  3722.                 for (i = 0; section_alist[i].name; i++)
  3723.                   if (strcmp (section_alist[i].name, "Top") == 0)
  3724.                     {
  3725.                       section_alist[i].level = this_section - 1;
  3726.                       break;
  3727.                     }
  3728.               }
  3729.           }
  3730.         input_text_offset = orig_offset;
  3731.       }
  3732.     }
  3733. }
  3734.  
  3735. /* The remainder of the text on this line is an unnumbered heading. */
  3736. cm_unnumbered ()
  3737. {
  3738.   cm_chapter ();
  3739. }
  3740.  
  3741. /* The remainder of the text on this line is an unnumbered section heading. */
  3742. cm_unnumberedsec ()
  3743. {
  3744.   cm_section ();
  3745. }
  3746.  
  3747. /* The remainder of the text on this line is an unnumbered
  3748.    subsection heading. */
  3749. cm_unnumberedsubsec ()
  3750. {
  3751.   cm_subsection ();
  3752. }
  3753.  
  3754. /* The remainder of the text on this line is an unnumbered
  3755.    subsubsection heading. */
  3756. cm_unnumberedsubsubsec ()
  3757. {
  3758.   cm_subsubsection ();
  3759. }
  3760.  
  3761. /* The remainder of the text on this line is an appendix heading. */
  3762. cm_appendix ()
  3763. {
  3764.   cm_chapter ();
  3765. }
  3766.  
  3767. /* The remainder of the text on this line is an appendix section heading. */
  3768. cm_appendixsec ()
  3769. {
  3770.   cm_section ();
  3771. }
  3772.  
  3773. /* The remainder of the text on this line is an appendix subsection heading. */
  3774. cm_appendixsubsec ()
  3775. {
  3776.   cm_subsection ();
  3777. }
  3778.  
  3779. /* The remainder of the text on this line is an appendix
  3780.    subsubsection heading. */
  3781. cm_appendixsubsubsec ()
  3782. {
  3783.   cm_subsubsection ();
  3784. }
  3785.  
  3786. /* Compatibility functions substitute for chapter, section, etc. */
  3787. cm_majorheading ()
  3788. {
  3789.   cm_chapheading ();
  3790. }
  3791.  
  3792. cm_chapheading ()
  3793. {
  3794.   cm_chapter ();
  3795. }
  3796.  
  3797. cm_heading ()
  3798. {
  3799.   cm_section ();
  3800. }
  3801.  
  3802. cm_subheading ()
  3803. {
  3804.   cm_subsection ();
  3805. }
  3806.  
  3807. cm_subsubheading ()
  3808. {
  3809.   cm_subsubsection ();
  3810. }
  3811.  
  3812.  
  3813. /* **************************************************************** */
  3814. /*                                                                  */
  3815. /*                 Adding nodes, and making tags                    */
  3816. /*                                                                  */
  3817. /* **************************************************************** */
  3818.  
  3819. /* Start a new tag table. */
  3820. init_tag_table ()
  3821. {
  3822.   while (tag_table != (TAG_ENTRY *) NULL)
  3823.     {
  3824.       TAG_ENTRY *temp = tag_table;
  3825.       free (temp->node);
  3826.       free (temp->prev);
  3827.       free (temp->next);
  3828.       free (temp->up);
  3829.       tag_table = tag_table->next_ent;
  3830.       free (temp);
  3831.     }
  3832. }
  3833.  
  3834. write_tag_table ()
  3835. {
  3836.   return (write_tag_table_internal (0));        /* Not indirect. */
  3837. }
  3838.  
  3839. write_tag_table_indirect ()
  3840. {
  3841.   return (write_tag_table_internal (1));
  3842. }
  3843.  
  3844. /* Write out the contents of the existing tag table.
  3845.    INDIRECT_P says how to format the output. */
  3846. write_tag_table_internal (indirect_p)
  3847.      int indirect_p;
  3848. {
  3849.   TAG_ENTRY *node = tag_table;
  3850.   int old_indent = no_indent;
  3851.  
  3852.   no_indent = 1;
  3853.   filling_enabled = 0;
  3854.   must_start_paragraph = 0;
  3855.   close_paragraph ();
  3856.  
  3857.   if (!indirect_p)
  3858.     {
  3859.       no_indent = 1;
  3860.       insert ('\n');
  3861.     }
  3862.   if (!no_headers)
  3863.       add_word_args ("\037\nTag Table:\n%s", indirect_p ? "(Indirect)\n" : "");
  3864.  
  3865.   if (!no_headers)
  3866.      {
  3867.      while (node != (TAG_ENTRY *) NULL)
  3868.        {
  3869.          add_word_args ("Node: %s\177%d\n", node->node, node->position);
  3870.           node = node->next_ent;
  3871.        }
  3872.        add_word ("\037\nEnd Tag Table\n");
  3873.      }
  3874.   flush_output ();
  3875.   no_indent = old_indent;
  3876. }
  3877.  
  3878. char *
  3879. get_node_token ()
  3880. {
  3881.   char *string;
  3882.  
  3883.   get_until_in_line (",", &string);
  3884.  
  3885.   if (curchar () == ',')
  3886.     input_text_offset++;
  3887.  
  3888.   canon_white (string);
  3889.  
  3890.   /* Allow things like @@nodename. */
  3891.   normalize_node_name (string);
  3892.  
  3893.   return (string);
  3894. }
  3895.  
  3896. /* Given a node name in STRING, remove double @ signs, replacing them
  3897.    with just one. */
  3898. normalize_node_name (string)
  3899.      char *string;
  3900. {
  3901.   register int i, l = strlen (string);
  3902.  
  3903.   for (i = 0; i < l; i++)
  3904.     {
  3905.       if (string[i] == '@' && string[i + 1] == '@')
  3906.         {
  3907.           strncpy (string + i, string + i + 1, l - i);
  3908.           l--;
  3909.         }
  3910.       else if (amiga_guide)
  3911.         {
  3912.           if (string[i] == '/')
  3913.             string[i] = '-';
  3914.           else
  3915.           if (string[i] == '\"')
  3916.             string[i] = '`';
  3917.  
  3918.         }
  3919.     }
  3920. }
  3921.  
  3922. /* Look up NAME in the tag table, and return the associated
  3923.    tag_entry.  If the node is not in the table return NULL. */
  3924. TAG_ENTRY *
  3925. find_node (name)
  3926.      char *name;
  3927. {
  3928.   TAG_ENTRY *tag = tag_table;
  3929.  
  3930.   while (tag != (TAG_ENTRY *) NULL)
  3931.     {
  3932.       if (stricmp (tag->node, name) == 0)
  3933.         return (tag);
  3934.       tag = tag->next_ent;
  3935.     }
  3936.   return ((TAG_ENTRY *) NULL);
  3937. }
  3938.  
  3939. /* Remember NODE and associates. */
  3940. remember_node (node, prev, next, up, position, line_no, no_warn)
  3941.      char *node, *prev, *next, *up;
  3942.      int position, line_no, no_warn;
  3943. {
  3944.   /* Check for existence of this tag already. */
  3945.   if (validating)
  3946.     {
  3947.       register TAG_ENTRY *tag = find_node (node);
  3948.       if (tag)
  3949.         {
  3950.           line_error ("Node `%s' multiply defined (%d is first definition)",
  3951.                       node, tag->line_no);
  3952.           return;
  3953.         }
  3954.     }
  3955.  
  3956.   /* First, make this the current node. */
  3957.   current_node = node;
  3958.  
  3959.   /* Now add it to the list. */
  3960.   {
  3961.     TAG_ENTRY *new = (TAG_ENTRY *) xmalloc (sizeof (TAG_ENTRY));
  3962.     new->node = node;
  3963.     new->prev = prev;
  3964.     new->next = next;
  3965.     new->up = up;
  3966.     new->position = position;
  3967.     new->line_no = line_no;
  3968.     new->filename = node_filename;
  3969.     new->touched = 0;           /* not yet referenced. */
  3970.     new->flags = 0;
  3971.     if (no_warn)
  3972.       new->flags |= NO_WARN;
  3973.     new->next_ent = tag_table;
  3974.     tag_table = new;
  3975.   }
  3976. }
  3977.  
  3978. /* The order is: nodename, nextnode, prevnode, upnode.
  3979.    If all of the NEXT, PREV, and UP fields are empty, they are defaulted.
  3980.    You must follow a node command which has those fields defaulted
  3981.    with a sectioning command (e.g. @chapter) giving the "level" of that node.
  3982.    It is an error not to do so.
  3983.    The defaults come from the menu in this nodes parent. */
  3984. cm_node ()
  3985. {
  3986.   char *node, *prev, *next, *up;
  3987.   int new_node_pos, defaulting, this_section, no_warn = 0;
  3988.   static amiga_guide_first_node = 1;
  3989.   extern int already_outputting_pending_notes;
  3990.  
  3991.   if (strcmp (command, "nwnode") == 0)
  3992.     no_warn = 1;
  3993.  
  3994.   /* Get rid of unmatched brace arguments from previous commands. */
  3995.   discard_braces ();
  3996.  
  3997.   /* There also might be insertions left lying around that haven't been
  3998.      ended yet.  Do that also. */
  3999.   discard_insertions ();
  4000.  
  4001.   if (!already_outputting_pending_notes)
  4002.     {
  4003.       close_paragraph ();
  4004.       output_pending_notes ();
  4005.       free_pending_notes ();
  4006.     }
  4007.  
  4008.   filling_enabled = indented_fill = 0;
  4009.   new_node_pos = output_position + 1;
  4010.   current_footnote_number = 1;
  4011.  
  4012.   node = get_node_token ();
  4013.   next = get_node_token ();
  4014.   prev = get_node_token ();
  4015.   up = get_node_token ();
  4016.  
  4017.   no_indent = 1;
  4018.  
  4019.   if (!no_headers)
  4020.     {
  4021.       if (amiga_guide)
  4022.         /* if amiga_guide_first_node is set to 1, then we are
  4023.            considering the first node of the database. */
  4024.         {
  4025.           if (amiga_guide_first_node)
  4026.             {
  4027.               add_word_args ("\n@Node Main \"%s\"", output_filename);
  4028.               amiga_guide_first_node = 0;
  4029.             }
  4030.           else
  4031.             {
  4032.               add_word_args("\n@EndNode\n\n");
  4033.               add_word_args ("@Node \"%s\" \"%s/%s\"",node,pretty_output_filename,node);
  4034.             }
  4035.         }
  4036.       else
  4037.         add_word_args ("\037\nFile: %s,  Node: %s", pretty_output_filename, node);
  4038.     }
  4039.  
  4040.   /* Check for defaulting of this node's next, prev, and up fields. */
  4041.   defaulting = ((strlen (next) == 0) &&
  4042.                 (strlen (prev) == 0) &&
  4043.                 (strlen (up) == 0));
  4044.  
  4045.   this_section = what_section (input_text + input_text_offset);
  4046.  
  4047.   /* If we are defaulting, then look at the immediately following
  4048.      sectioning command (error if none) to determine the node's
  4049.      level.  Find the node that contains the menu mentioning this node
  4050.      that is one level up (error if not found).  That node is the "Up"
  4051.      of this node.  Default the "Next" and "Prev" from the menu. */
  4052.   if (defaulting)
  4053.     {
  4054.       NODE_REF *last_ref = (NODE_REF *)NULL;
  4055.       NODE_REF *ref = node_references;
  4056.  
  4057.       if (this_section < 0)
  4058.         {
  4059.           char *polite_section_name = "top";
  4060.           int i;
  4061.  
  4062.           for (i = 0; section_alist[i].name; i++)
  4063.             if (section_alist[i].level == current_section + 1)
  4064.               {
  4065.                 polite_section_name = section_alist[i].name;
  4066.                 break;
  4067.               }
  4068.  
  4069.           line_error
  4070.             ("Node `%s' requires a sectioning command (e.g. @%s)",
  4071.              node, polite_section_name);
  4072.         }
  4073.       else
  4074.         {
  4075.           if (stricmp (node, "Top") == 0)
  4076.             {
  4077.               /* Default the NEXT pointer to be the first menu item in
  4078.                  this node, if there is a menu in this node. */
  4079.               {
  4080.                 int orig_offset, orig_size;
  4081.                 char *glean_node_from_menu ();
  4082.  
  4083.                 orig_offset = input_text_offset;
  4084.                 orig_size = search_forward ("\n@node ", orig_offset);
  4085.  
  4086.                 if (orig_size < 0)
  4087.                   orig_size = size_of_input_text;
  4088.  
  4089.                 input_text_offset = search_forward ("\n@menu", orig_offset);
  4090.                 if (input_text_offset > -1)
  4091.                   {
  4092.                     input_text_offset =
  4093.                       search_forward ("\n* ", input_text_offset);
  4094.  
  4095.                     if (input_text_offset > -1 )
  4096.                       next = glean_node_from_menu (0);
  4097.  
  4098.                     if (next && !amiga_guide)
  4099.                       {
  4100.                         prev = savestring ("(DIR)");
  4101.                         up = savestring ("(DIR)");
  4102.                       }
  4103.                   }
  4104.                 input_text_offset = orig_offset;
  4105.               }
  4106.             }
  4107.  
  4108.           while (ref)
  4109.             {
  4110.               if (ref->section == (this_section - 1) &&
  4111.                   ref->type == menu_reference &&
  4112.                   stricmp (ref->node, node) == 0)
  4113.                 {
  4114.                   char *containing_node = ref->containing_node;
  4115.  
  4116.                   free (up);
  4117.                   up = savestring (containing_node);
  4118.  
  4119.                   if (last_ref && last_ref->type == menu_reference &&
  4120.                       strcmp
  4121.                       (last_ref->containing_node, containing_node) == 0)
  4122.                     {
  4123.                       free (next);
  4124.                       next = savestring (last_ref->node);
  4125.                     }
  4126.  
  4127.                   while ((ref->section == this_section - 1) &&
  4128.                          (ref->next) &&
  4129.                          (ref->next->type != menu_reference))
  4130.                     ref = ref->next;
  4131.  
  4132.                   if (ref->next && ref->type == menu_reference &&
  4133.                       strcmp
  4134.                       (ref->next->containing_node, containing_node) == 0)
  4135.                     {
  4136.                       free (prev);
  4137.                       prev = savestring (ref->next->node);
  4138.                     }
  4139.                   else if (!ref->next &&
  4140.                            stricmp (ref->containing_node, "Top") == 0)
  4141.                     {
  4142.                       free (prev);
  4143.                       prev = savestring (ref->containing_node);
  4144.                     }
  4145.                   break;
  4146.                 }
  4147.               last_ref = ref;
  4148.               ref = ref->next;
  4149.             }
  4150.         }
  4151.     }
  4152.  
  4153.   if (!no_headers)
  4154.     {
  4155.       if (amiga_guide)
  4156.         {
  4157.           if (*next)
  4158.              {
  4159.                 if (stricmp (next, "Top") == 0)
  4160.                    add_word_args ("\n@Next \"Main\"");
  4161.                 else
  4162.                    add_word_args ("\n@Next \"%s\"", next);
  4163.              }
  4164.  
  4165.           if (*prev)
  4166.             {
  4167.               if (stricmp (prev, "(DIR)") != 0)
  4168.                 {
  4169.                 if (stricmp (prev, "Top") == 0)
  4170.                    add_word_args ("\n@Prev \"Main\"");
  4171.                  else
  4172.                     add_word_args ("\n@Prev \"%s\"", prev);
  4173.                 }
  4174.              }
  4175.           if (*up)
  4176.              {
  4177.                if (stricmp (up, "(DIR)") != 0)
  4178.                 {
  4179.                 if (stricmp (up, "Top") == 0)
  4180.                    add_word_args ("\n@Toc \"Main\"");
  4181.                 else
  4182.                    add_word_args ("\n@Toc \"%s\"", up);
  4183.                 }
  4184.              }
  4185.         }
  4186.       else
  4187.         {
  4188.           if (*next)
  4189.             add_word_args (",  Next: %s", next);
  4190.  
  4191.           if (*prev)
  4192.             add_word_args (",  Prev: %s", prev);
  4193.  
  4194.           if (*up)
  4195.             add_word_args (",  Up: %s", up);
  4196.         }
  4197.     }
  4198.  
  4199.   insert ('\n');
  4200.   close_paragraph ();
  4201.   no_indent = 0;
  4202.  
  4203.   if (!*node)
  4204.     {
  4205.       line_error ("No node name specified for `@%s' command", command);
  4206.       free (node);
  4207.       free (next);
  4208.       free (prev);
  4209.       free (up);
  4210.     }
  4211.   else
  4212.     {
  4213.       if (!*next) { free (next); next = (char *)NULL; }
  4214.       if (!*prev) { free (prev); prev = (char *)NULL; }
  4215.       if (!*up) { free (up); up = (char *)NULL; }
  4216.       remember_node (node, prev, next, up, new_node_pos, line_number, no_warn);
  4217.     }
  4218.  
  4219.   /* Change the section only if there was a sectioning command. */
  4220.   if (this_section >= 0)
  4221.     current_section = this_section;
  4222.  
  4223.   filling_enabled = 1;
  4224. }
  4225.  
  4226. /* Validation of an info file.
  4227.    Scan through the list of tag entrys touching the Prev, Next, and Up
  4228.    elements of each.  It is an error not to be able to touch one of them,
  4229.    except in the case of external node references, such as "(DIR)".
  4230.  
  4231.    If the Prev is different from the Up,
  4232.    then the Prev node must have a Next pointing at this node.
  4233.  
  4234.    Every node except Top must have an Up.
  4235.    The Up node must contain some sort of reference, other than a Next,
  4236.    to this node.
  4237.  
  4238.    If the Next is different from the Next of the Up,
  4239.    then the Next node must have a Prev pointing at this node. */
  4240. validate_file (filename, tag_table)
  4241.      char *filename;
  4242.      TAG_ENTRY *tag_table;
  4243. {
  4244.   char *old_input_filename = input_filename;
  4245.   TAG_ENTRY *tags = tag_table;
  4246.  
  4247.   while (tags != (TAG_ENTRY *) NULL)
  4248.     {
  4249.       register TAG_ENTRY *temp_tag;
  4250.  
  4251.       input_filename = tags->filename;
  4252.       line_number = tags->line_no;
  4253.  
  4254.       /* If this node has a Next, then make sure that the Next exists. */
  4255.       if (tags->next)
  4256.         {
  4257.           validate (tags->next, tags->line_no, "Next");
  4258.  
  4259.           /* If the Next node exists, and there is no Up, then make
  4260.              sure that the Prev of the Next points back. */
  4261.           if (temp_tag = find_node (tags->next))
  4262.             {
  4263.               char *prev = temp_tag->prev;
  4264.               if (!prev || (stricmp (prev, tags->node) != 0))
  4265.                 {
  4266.                   line_error
  4267.                     ("Node `%s''s Next field not pointed back to", tags->node);
  4268.                   line_number = temp_tag->line_no;
  4269.                   input_filename = temp_tag->filename;
  4270.                   line_error
  4271.                     ("This node (`%s') is the one with the bad `Prev'",
  4272.                      temp_tag->node);
  4273.                   input_filename = tags->filename;
  4274.                   line_number = tags->line_no;
  4275.                   temp_tag->flags |= PREV_ERROR;
  4276.                 }
  4277.             }
  4278.         }
  4279.  
  4280.       /* Validate the Prev field if there is one, and we haven't already
  4281.          complained about it in some way.  You don't have to have a Prev
  4282.          field at this stage. */
  4283.       if (!(tags->flags & PREV_ERROR) && tags->prev)
  4284.         {
  4285.           int valid = validate (tags->prev, tags->line_no, "Prev");
  4286.  
  4287.           if (!valid)
  4288.             tags->flags |= PREV_ERROR;
  4289.           else
  4290.             {
  4291.               /* If the Prev field is not the same as the Up field,
  4292.                  then the node pointed to by the Prev field must have
  4293.                  a Next field which points to this node. */
  4294.               if (tags->up && (stricmp (tags->prev, tags->up) != 0))
  4295.                 {
  4296.                   temp_tag = find_node (tags->prev);
  4297.                   if (!temp_tag->next ||
  4298.                       (stricmp (temp_tag->next, tags->node) != 0))
  4299.                     {
  4300.                       line_error ("Node `%s''s Prev field not pointed back to",
  4301.                                   tags->node);
  4302.                       line_number = temp_tag->line_no;
  4303.                       input_filename = temp_tag->filename;
  4304.                       line_error
  4305.                         ("This node (`%s') is the one with the bad `Next'",
  4306.                          temp_tag->node);
  4307.                       input_filename = tags->filename;
  4308.                       line_number = tags->line_no;
  4309.                       temp_tag->flags |= NEXT_ERROR;
  4310.                     }
  4311.                 }
  4312.             }
  4313.         }
  4314.  
  4315.       if (!tags->up && (stricmp (tags->node, "Top") != 0))
  4316.         line_error ("Node `%s' is missing an \"Up\" field", tags->node);
  4317.       else if (tags->up)
  4318.         {
  4319.           int valid = validate (tags->up, tags->line_no, "Up");
  4320.  
  4321.           /* If node X has Up: Y, then warn if Y fails to have a menu item
  4322.              or note pointing at X, if Y isn't of the form "(Y)". */
  4323.           if (valid && *tags->up != '(')
  4324.             {
  4325.               NODE_REF *nref, *tref, *list;
  4326.               NODE_REF *find_node_reference ();
  4327.  
  4328.               tref = (NODE_REF *) NULL;
  4329.               list = node_references;
  4330.  
  4331.               for (;;)
  4332.                 {
  4333.                   if (!(nref = find_node_reference (tags->node, list)))
  4334.                     break;
  4335.  
  4336.                   if (stricmp (nref->containing_node, tags->up) == 0)
  4337.                     {
  4338.                       if (nref->type != menu_reference)
  4339.                         {
  4340.                           tref = nref;
  4341.                           list = nref->next;
  4342.                         }
  4343.                       else
  4344.                         break;
  4345.                     }
  4346.                   list = nref->next;
  4347.                 }
  4348.  
  4349.               if (!nref)
  4350.                 {
  4351.                   temp_tag = find_node (tags->up);
  4352.                   line_number = temp_tag->line_no;
  4353.                   filename = temp_tag->filename;
  4354.                   if (!tref)
  4355.                     line_error (
  4356. "`%s' has an Up field of `%s', but `%s' has no menu item for `%s'",
  4357.                                 tags->node, tags->up, tags->up, tags->node);
  4358.                   line_number = tags->line_no;
  4359.                   filename = tags->filename;
  4360.                 }
  4361.             }
  4362.         }
  4363.       tags = tags->next_ent;
  4364.     }
  4365.  
  4366.   validate_other_references (node_references);
  4367.   /* We have told the user about the references which didn't exist.
  4368.      Now tell him about the nodes which aren't referenced. */
  4369.  
  4370.   tags = tag_table;
  4371.   while (tags != (TAG_ENTRY *) NULL)
  4372.     {
  4373.       /* Special hack.  If the node in question appears to have
  4374.          been referenced more than REFERENCE_WARNING_LIMIT times,
  4375.          give a warning. */
  4376.       if (tags->touched > reference_warning_limit)
  4377.         {
  4378.           input_filename = tags->filename;
  4379.           line_number = tags->line_no;
  4380.           warning ("Node `%s' has been referenced %d times",
  4381.                    tags->node, tags->touched);
  4382.         }
  4383.  
  4384.       if (tags->touched == 0)
  4385.         {
  4386.           input_filename = tags->filename;
  4387.           line_number = tags->line_no;
  4388.  
  4389.           /* Notice that the node "Top" is special, and doesn't have to
  4390.              be referenced. */
  4391.           if (stricmp (tags->node, "Top") != 0)
  4392.             warning ("Unreferenced node `%s'", tags->node);
  4393.         }
  4394.       tags = tags->next_ent;
  4395.     }
  4396.   input_filename = old_input_filename;
  4397. }
  4398.  
  4399. /* Return 1 if tag correctly validated, or 0 if not. */
  4400. validate (tag, line, label)
  4401.      char *tag;
  4402.      int line;
  4403.      char *label;
  4404. {
  4405.   TAG_ENTRY *result;
  4406.  
  4407.   /* If there isn't a tag to verify, or if the tag is in another file,
  4408.      then it must be okay. */
  4409.   if (!tag || !*tag || *tag == '(')
  4410.     return (1);
  4411.  
  4412.   /* Otherwise, the tag must exist. */
  4413.   result = find_node (tag);
  4414.  
  4415.   if (!result)
  4416.     {
  4417.       line_number = line;
  4418.       line_error (
  4419. "Validation error.  `%s' field points to node `%s', which doesn't exist",
  4420.                   label, tag);
  4421.       return (0);
  4422.     }
  4423.   result->touched++;
  4424.   return (1);
  4425. }
  4426.  
  4427. /* Split large output files into a series of smaller files.  Each file
  4428.    is pointed to in the tag table, which then gets written out as the
  4429.    original file.  The new files have the same name as the original file
  4430.    with a "-num" attached.  SIZE is the largest number of bytes to allow
  4431.    in any single split file. */
  4432. split_file (filename, size)
  4433.      char *filename;
  4434.      int size;
  4435. {
  4436.   char *root_filename, *root_pathname;
  4437.   char *the_file, *filename_part ();
  4438.   struct stat fileinfo;
  4439.   char *the_header;
  4440.   int header_size;
  4441.  
  4442.   /* Can only do this to files with tag tables. */
  4443.   if (!tag_table)
  4444.     return;
  4445.  
  4446.   if (size == 0)
  4447.     size = DEFAULT_SPLIT_SIZE;
  4448.  
  4449.   if ((stat (filename, &fileinfo) != 0) ||
  4450.       (fileinfo.st_size < SPLIT_SIZE_THRESHOLD))
  4451.     return;
  4452.  
  4453.   the_file = find_and_load (filename);
  4454.   if (!the_file)
  4455.     return;
  4456.  
  4457.   root_filename = filename_part (filename);
  4458.   root_pathname = pathname_part (filename);
  4459.  
  4460.   if (!root_pathname)
  4461.     root_pathname = savestring ("");
  4462.  
  4463.   /* Start splitting the file.  Walk along the tag table
  4464.      outputting sections of the file.  When we have written
  4465.      all of the nodes in the tag table, make the top-level
  4466.      pointer file, which contains indirect pointers and
  4467.      tags for the nodes. */
  4468.   {
  4469.     int which_file = 1;
  4470.     TAG_ENTRY *tags = tag_table;
  4471.     char *indirect_info = (char *)NULL;
  4472.  
  4473.     /* Remember the `header' of this file.  The first tag in the file is
  4474.        the bottom of the header; the top of the file is the start. */
  4475.     the_header = (char *)xmalloc (1 + (header_size = (tags->position - 2)));
  4476.     bcopy (the_file, the_header, header_size);
  4477.  
  4478.     while (tags)
  4479.       {
  4480.         int file_top, file_bot, limit;
  4481.  
  4482.         /* Have to include the Control-_. */
  4483.         file_top = file_bot = tags->position - 2;
  4484.         limit = file_top + size;
  4485.  
  4486.         /* If the rest of this file is only one node, then
  4487.            that is the entire subfile. */
  4488.         if (!tags->next_ent)
  4489.           {
  4490.             int i = tags->position + 1;
  4491.             char last_char = the_file[i];
  4492.  
  4493.             while (i < fileinfo.st_size)
  4494.               {
  4495.                 if ((the_file[i] == '\037') &&
  4496.                     ((last_char == '\n') ||
  4497.                      (last_char == '\014')))
  4498.                   break;
  4499.                 else
  4500.                   last_char = the_file[i];
  4501.                 i++;
  4502.               }
  4503.             file_bot = i;
  4504.             tags = tags->next_ent;
  4505.             goto write_region;
  4506.           }
  4507.  
  4508.         /* Otherwise, find the largest number of nodes that can fit in
  4509.            this subfile. */
  4510.         for (; tags; tags = tags->next_ent)
  4511.           {
  4512.             if (!tags->next_ent)
  4513.               {
  4514.                 /* This entry is the last node.  Search forward for the end
  4515.                    of this node, and that is the end of this file. */
  4516.                 int i = tags->position + 1;
  4517.                 char last_char = the_file[i];
  4518.  
  4519.                 while (i < fileinfo.st_size)
  4520.                   {
  4521.                     if ((the_file[i] == '\037') &&
  4522.                         ((last_char == '\n') ||
  4523.                          (last_char == '\014')))
  4524.                       break;
  4525.                     else
  4526.                       last_char = the_file[i];
  4527.                     i++;
  4528.                   }
  4529.                 file_bot = i;
  4530.  
  4531.                 if (file_bot < limit)
  4532.                   {
  4533.                     tags = tags->next_ent;
  4534.                     goto write_region;
  4535.                   }
  4536.                 else
  4537.                   {
  4538.                     /* Here we want to write out everything before the last
  4539.                        node, and then write the last node out in a file
  4540.                        by itself. */
  4541.                     file_bot = tags->position;
  4542.                     goto write_region;
  4543.                   }
  4544.               }
  4545.  
  4546.             if (tags->next_ent->position > limit)
  4547.               {
  4548.                 if ((tags->position) - 2 == file_top)
  4549.                   tags = tags->next_ent;
  4550.                 file_bot = tags->position;
  4551.               write_region:
  4552.                 {
  4553.                   int fd;
  4554.                   char *split_file = (char *)
  4555.                     xmalloc (10
  4556.                              + strlen (root_pathname)
  4557.                              + strlen (root_filename));
  4558.                   sprintf (split_file,
  4559.                        "%s%s-%d", root_pathname, root_filename, which_file);
  4560.  
  4561.                   if (((fd = open (split_file, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0)
  4562.                       || (write (fd, the_header, header_size) != header_size)
  4563.                       || (write (fd, the_file + file_top, file_bot - file_top)
  4564.                           != (file_bot - file_top))
  4565.                       || ((close (fd)) < 0))
  4566.                     {
  4567.                       perror (split_file);
  4568.                       close (fd);
  4569.                       exit (FATAL);
  4570.                     }
  4571.  
  4572.                   if (!indirect_info)
  4573.                     {
  4574.                       indirect_info = the_file + file_top;
  4575.                       sprintf (indirect_info, "\037\nIndirect:\n");
  4576.                       indirect_info += strlen (indirect_info);
  4577.                     }
  4578.  
  4579.                   sprintf (indirect_info, "%s-%d: %d\n",
  4580.                            root_filename, which_file, file_top);
  4581.  
  4582.                   free (split_file);
  4583.                   indirect_info += strlen (indirect_info);
  4584.                   which_file++;
  4585.                   break;
  4586.                 }
  4587.               }
  4588.           }
  4589.       }
  4590.  
  4591.     /* We have sucessfully created the subfiles.  Now write out the
  4592.        original again.  We must use `output_stream', or
  4593.        write_tag_table_indirect () won't know where to place the output. */
  4594.     output_stream = fopen (filename, "w");
  4595.     if (!output_stream)
  4596.       {
  4597.         perror (filename);
  4598.         exit (FATAL);
  4599.       }
  4600.  
  4601.     {
  4602.       int distance = indirect_info - the_file;
  4603.       fwrite (the_file, 1, distance, output_stream);
  4604.  
  4605.       /* Inhibit newlines. */
  4606.       paragraph_is_open = 0;
  4607.  
  4608.       write_tag_table_indirect ();
  4609.       fclose (output_stream);
  4610.       free (the_header);
  4611.       free (the_file);
  4612.       return;
  4613.     }
  4614.   }
  4615. }
  4616.  
  4617. /* Some menu hacking.  This is used to remember menu references while
  4618.    reading the input file.  After the output file has been written, if
  4619.    validation is on, then we use the contents of NODE_REFERENCES as a
  4620.    list of nodes to validate. */
  4621. char *
  4622. reftype_type_string (type)
  4623.      enum reftype type;
  4624. {
  4625.   switch (type)
  4626.     {
  4627.     case menu_reference:
  4628.       return ("Menu");
  4629.     case followed_reference:
  4630.       return ("Followed-Reference");
  4631.     default:
  4632.       return ("Internal-bad-reference-type");
  4633.     }
  4634. }
  4635.  
  4636. /* Remember this node name for later validation use. */
  4637. remember_node_reference (node, line, type)
  4638.      char *node;
  4639.      int line;
  4640.      enum reftype type;
  4641. {
  4642.   NODE_REF *temp = (NODE_REF *) xmalloc (sizeof (NODE_REF));
  4643.  
  4644.   temp->next = node_references;
  4645.   temp->node = savestring (node);
  4646.   temp->line_no = line;
  4647.   temp->section = current_section;
  4648.   temp->type = type;
  4649.   temp->containing_node = savestring (current_node);
  4650.   temp->filename = node_filename;
  4651.  
  4652.   node_references = temp;
  4653. }
  4654.  
  4655. validate_other_references (ref_list)
  4656.      register NODE_REF *ref_list;
  4657. {
  4658.   char *old_input_filename = input_filename;
  4659.  
  4660.   while (ref_list != (NODE_REF *) NULL)
  4661.     {
  4662.       input_filename = ref_list->filename;
  4663.       validate (ref_list->node, ref_list->line_no,
  4664.                 reftype_type_string (ref_list->type));
  4665.       ref_list = ref_list->next;
  4666.     }
  4667.   input_filename = old_input_filename;
  4668. }
  4669.  
  4670. /* Find NODE in REF_LIST. */
  4671. NODE_REF *
  4672. find_node_reference (node, ref_list)
  4673.      char *node;
  4674.      register NODE_REF *ref_list;
  4675. {
  4676.   while (ref_list)
  4677.     {
  4678.       if (stricmp (node, ref_list->node) == 0)
  4679.         break;
  4680.       ref_list = ref_list->next;
  4681.     }
  4682.   return (ref_list);
  4683. }
  4684.  
  4685. free_node_references ()
  4686. {
  4687.   register NODE_REF *list, *temp;
  4688.  
  4689.   list = node_references;
  4690.  
  4691.   while (list)
  4692.     {
  4693.       temp = list;
  4694.       free (list->node);
  4695.       free (list->containing_node);
  4696.       list = list->next;
  4697.       free (temp);
  4698.     }
  4699.   node_references = (NODE_REF *) NULL;
  4700. }
  4701.  
  4702.   /* This function gets called at the start of every line while inside of
  4703.      a menu.  It checks to see if the line starts with "* ", and if so,
  4704.      remembers the node reference that this menu refers to.
  4705.      input_text_offset is at the \n just before the line start. */
  4706. #define menu_starter "* "
  4707. char *
  4708. glean_node_from_menu (remember_reference)
  4709.      int remember_reference;
  4710. {
  4711.   int i, orig_offset = input_text_offset;
  4712.   char *nodename;
  4713.  
  4714.   if (strncmp (&input_text[input_text_offset + 1],
  4715.                menu_starter,
  4716.                strlen (menu_starter)) != 0)
  4717.     return ((char *)NULL);
  4718.   else
  4719.     input_text_offset += strlen (menu_starter) + 1;
  4720.  
  4721.   get_until_in_line (":", &nodename);
  4722.   if (curchar () == ':')
  4723.     input_text_offset++;
  4724.   canon_white (nodename);
  4725.  
  4726.   if (curchar () == ':')
  4727.     goto save_node;
  4728.  
  4729.   free (nodename);
  4730.   get_rest_of_line (&nodename);
  4731.  
  4732.   /* Special hack: If the nodename follows the menu item name,
  4733.      then we have to read the rest of the line in order to find
  4734.      out what the nodename is.  But we still have to read the
  4735.      line later, in order to process any formatting commands that
  4736.      might be present.  So un-count the carriage return that has just
  4737.      been counted. */
  4738.   line_number--;
  4739.  
  4740.   canon_white (nodename);
  4741.   for (i = 0; i < strlen (nodename); i++)
  4742.     {
  4743.       if (nodename[i] == '\t' ||
  4744.           nodename[i] == '.' ||
  4745.           nodename[i] == ',')
  4746.         {
  4747.           nodename[i] = '\0';
  4748.           break;
  4749.         }
  4750.     }
  4751. save_node:
  4752.   input_text_offset = orig_offset;
  4753.   normalize_node_name (nodename);
  4754.   i = strlen (nodename);
  4755.   if (i && nodename[i - 1] == ':')
  4756.     nodename[i - 1] = '\0';
  4757.  
  4758.   if (remember_reference)
  4759.     {
  4760.       remember_node_reference (nodename, line_number, menu_reference);
  4761.       free (nodename);
  4762.       return ((char *)NULL);
  4763.     }
  4764.   else
  4765.     return (nodename);
  4766. }
  4767.  
  4768. int
  4769. amiga_guide_convert_menu ()
  4770. {
  4771.   int i;
  4772.   char *gadgetname;
  4773.  
  4774.   if (strncmp (&input_text[input_text_offset + 1],
  4775.                menu_starter,
  4776.                strlen (menu_starter)) != 0)
  4777.     return(0);
  4778.   else
  4779.     input_text_offset += strlen (menu_starter) + 1;
  4780.  
  4781.   get_until_in_line (":", &gadgetname);
  4782.  
  4783.   input_text_offset++;
  4784.  
  4785.   if (curchar () == ':')
  4786.     {
  4787.       input_text_offset++;
  4788.       canon_white (gadgetname);
  4789.       normalize_node_name (gadgetname);
  4790.       in_amiga_guide_button = 1;
  4791.       output_column -= (strlen("@{\"\" Link \"\"}") +
  4792.                         strlen(gadgetname));
  4793.       execute_string ("\n@w{ @@@{\" %s \" Link \"%s\"@} }", gadgetname, gadgetname);
  4794.       in_amiga_guide_button = 0;
  4795.     }
  4796.   else
  4797.     {
  4798.       char *set_p(char *);
  4799.  
  4800.       char *nodename;
  4801.       int nodestart;
  4802.  
  4803.       nodestart = input_text_offset;
  4804.       get_until_in_line (".", &nodename);
  4805.  
  4806.   /* Special hack: If the nodename follows the menu item name,
  4807.      then we have to read the rest of the line in order to find
  4808.      out what the nodename is.  But we still have to read the
  4809.      line later, in order to process any formatting commands that
  4810.      might be present.  So un-count the carriage return that has just
  4811.      been counted.
  4812.       line_number--; */
  4813.  
  4814.       canon_white (nodename);
  4815.       normalize_node_name (nodename);
  4816.  
  4817.       if (*nodename == '(')
  4818.         {
  4819.           for(i=1; i<strlen(nodename); i++)
  4820.             if (nodename[i] == ')') break;
  4821.  
  4822.           if (i == strlen(nodename)) return(0);
  4823.  
  4824.           nodename[i] = 0;
  4825.  
  4826.           in_amiga_guide_button = 1;
  4827.           output_column -= (strlen("@{\"\" Link \"/\"}") +
  4828.                             strlen(nodename+1)+strlen(nodename+i+1));
  4829.           execute_string ("\n@w{ @@@{\" %s \" Link \"%s/%s\"@} }", gadgetname, nodename+1, nodename+i+1);
  4830.           in_amiga_guide_button = 0;
  4831.  
  4832.         }
  4833.       else
  4834.         {
  4835.           in_amiga_guide_button = 1;
  4836.           output_column -= (strlen("@{\"\" Link \"\"}") +
  4837.                             strlen(nodename));
  4838.           execute_string ("\n@w{ @@@{\" %s \" Link \"%s\"@} }", gadgetname, nodename);
  4839.           in_amiga_guide_button = 0;
  4840.         }
  4841.  
  4842.       if (set_p("AmigaGuide-expand-menus"))
  4843.         {
  4844.           input_text_offset = nodestart;
  4845.         }
  4846.       else
  4847.         {
  4848.           for(i=0; i<input_text_offset-nodestart; i++) insert(' ');
  4849.           input_text_offset++;
  4850.         }
  4851.  
  4852.       free(nodename);
  4853.     }
  4854.  
  4855.   free(gadgetname);
  4856.   return(1);
  4857. }
  4858.  
  4859.  
  4860. cm_menu ()
  4861. {
  4862.   begin_insertion (menu);
  4863. }
  4864.  
  4865.  
  4866. /* **************************************************************** */
  4867. /*                                                                  */
  4868. /*                      Cross Reference Hacking                     */
  4869. /*                                                                  */
  4870. /* **************************************************************** */
  4871.  
  4872. char *
  4873. get_xref_token ()
  4874. {
  4875.   char *string;
  4876.  
  4877.   get_until_in_braces (",", &string);
  4878.   if (curchar () == ',')
  4879.     input_text_offset++;
  4880.   fix_whitespace (string);
  4881.   return (string);
  4882. }
  4883.  
  4884. int px_ref_flag = 0;            /* Controls initial output string. */
  4885. int ref_flag = 0;               /* Controls initial output string, too. */
  4886.  
  4887. /* Make a cross reference. */
  4888. cm_xref (arg)
  4889. {
  4890.   if (arg == START)
  4891.     {
  4892.       char *arg1, *arg2, *arg3, *arg4, *arg5;
  4893.  
  4894.       arg1 = get_xref_token ();
  4895.       arg2 = get_xref_token ();
  4896.       arg3 = get_xref_token ();
  4897.       arg4 = get_xref_token ();
  4898.       arg5 = get_xref_token ();
  4899.  
  4900.       if (amiga_guide) normalize_node_name(arg1);
  4901.  
  4902.       if (amiga_guide)
  4903.         {
  4904.           if (!ref_flag)
  4905.             add_word_args("%s", px_ref_flag ? "see " : "See ");
  4906.         }
  4907.       else
  4908.          add_word_args ("%s", px_ref_flag ? "*note " : "*Note ");
  4909.  
  4910.       if (*arg5 || *arg4)
  4911.         {
  4912.           char *node_name;
  4913.  
  4914.           if (!*arg2)
  4915.             {
  4916.               if (*arg3)
  4917.                 node_name = arg3;
  4918.               else
  4919.                 node_name = arg1;
  4920.             }
  4921.           else
  4922.             node_name = arg2;
  4923.  
  4924.           if (amiga_guide)
  4925.             {
  4926.               if (no_headers)
  4927.                 {
  4928.                   execute_string ("%s/%s", arg4, node_name);
  4929.                 }
  4930.               else
  4931.                 {
  4932.                   in_amiga_guide_button = 1;
  4933.                   output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"/\"}") +
  4934.                                     strlen(arg4)+strlen(arg1));
  4935.                   canon_white(node_name);
  4936.                   execute_string ("@w{@@@{\"%s\" Link \"%s/%s\"@}}", node_name, arg4, arg1);
  4937.                   in_amiga_guide_button = 0;
  4938.  
  4939.                 }
  4940.             }
  4941.           else
  4942.             execute_string ("%s: (%s)%s", node_name, arg4, arg1);
  4943.           return;
  4944.         }
  4945.       else
  4946.         remember_node_reference (arg1, line_number, followed_reference);
  4947.  
  4948.       if (*arg3)
  4949.         {
  4950.           if (amiga_guide)
  4951.             {
  4952.               if (no_headers)
  4953.                 {
  4954.                   execute_string (arg1);
  4955.                 }
  4956.               else
  4957.                 {
  4958.                   in_amiga_guide_button = 1;
  4959.  
  4960.                   if (!*arg2)
  4961.                     {
  4962.                       output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  4963.                                         +strlen(arg1));
  4964.                       canon_white(arg3);
  4965.                       execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg3, arg1);
  4966.  
  4967.                     }
  4968.                   else
  4969.                     {
  4970.  
  4971.                       output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  4972.                                         +strlen(arg1));
  4973.                       canon_white(arg2);
  4974.                       execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg2, arg1);
  4975.  
  4976.                     }
  4977.  
  4978.                   in_amiga_guide_button = 0;
  4979.                 }
  4980.             }
  4981.           else
  4982.             {
  4983.               if (!*arg2)
  4984.                 execute_string ("%s: %s", arg3, arg1);
  4985.               else
  4986.                 execute_string ("%s: %s", arg2, arg1);
  4987.             }
  4988.  
  4989.           return;
  4990.         }
  4991.  
  4992.  
  4993.     if (amiga_guide)
  4994.       {
  4995.         if (no_headers)
  4996.           {
  4997.             execute_string (arg1);
  4998.           }
  4999.         else
  5000.           {
  5001.             in_amiga_guide_button = 1;
  5002.  
  5003.             if (*arg2)
  5004.               {
  5005.  
  5006.                 output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  5007.                                   +strlen(arg1));
  5008.                 canon_white(arg2);
  5009.                 execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg2, arg1);
  5010.  
  5011.               }
  5012.             else
  5013.               {
  5014.  
  5015.                 output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  5016.                                   +strlen(arg1));
  5017.                 canon_white(arg1);
  5018.                 execute_string ("@w{@@@{\"%s\" Link \"%s\"@}}", arg1, arg1);
  5019.  
  5020.               }
  5021.  
  5022.               in_amiga_guide_button = 0;
  5023.           }
  5024.       }
  5025.     else
  5026.       {
  5027.         if (*arg2)
  5028.           execute_string ("%s: %s", arg2, arg1);
  5029.         else
  5030.           execute_string ("%s::", arg1);
  5031.       }
  5032.  
  5033.     }
  5034.   else
  5035.     {
  5036.       /* Check to make sure that the next non-whitespace character is either
  5037.          a period or a comma. input_text_offset is pointing at the "}" which
  5038.          ended the xref or pxref command. */
  5039.       int temp = input_text_offset + 1;
  5040.  
  5041.       if (output_paragraph[output_paragraph_offset - 2] == ':' &&
  5042.           output_paragraph[output_paragraph_offset - 1] == ':')
  5043.         return;
  5044.       while (temp < size_of_input_text)
  5045.         {
  5046.           if (cr_or_whitespace (input_text[temp]))
  5047.             temp++;
  5048.           else
  5049.             {
  5050.               if (input_text[temp] == '.' ||
  5051.                   input_text[temp] == ',' ||
  5052.                   input_text[temp] == '\t')
  5053.                 return;
  5054.               else
  5055.                 {
  5056.                   line_error ("Cross-reference must be terminated with a period or a comma");
  5057.                   return;
  5058.                 }
  5059.             }
  5060.         }
  5061.     }
  5062. }
  5063.  
  5064. cm_pxref (arg)
  5065.      int arg;
  5066. {
  5067.   if (arg == START)
  5068.     {
  5069.       px_ref_flag++;
  5070.       cm_xref (arg);
  5071.       px_ref_flag--;
  5072.     }
  5073.   else
  5074.     if (!amiga_guide)
  5075.       {
  5076.         add_char ('.');
  5077.       }
  5078. }
  5079.  
  5080. cm_ref (arg)
  5081.      int arg;
  5082. {
  5083.   if (arg == START)
  5084.     {
  5085.       ref_flag++;
  5086.       cm_xref (arg);
  5087.       ref_flag--;
  5088.     }
  5089. }
  5090.  
  5091. cm_inforef (arg)
  5092.      int arg;
  5093. {
  5094.   if (arg == START)
  5095.     {
  5096.       char *node, *pname, *file;
  5097.  
  5098.       node = get_xref_token ();
  5099.       pname = get_xref_token ();
  5100.       file = get_xref_token ();
  5101.  
  5102.       if (amiga_guide)
  5103.         {
  5104.           add_word("See ");
  5105.  
  5106.           if (no_headers)
  5107.             {
  5108.               execute_string ("%s/%s", file, node);
  5109.             }
  5110.           else
  5111.             {
  5112.               in_amiga_guide_button = 1;
  5113.               output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"/\"}") +
  5114.                                 strlen(file)+strlen(node));
  5115.               canon_white(pname);
  5116.               execute_string ("@w{@@@{\"%s\" Link \"%s/%s\"@}}", pname, file, node);
  5117.               in_amiga_guide_button = 0;
  5118.  
  5119.             }
  5120.         }
  5121.       else
  5122.         execute_string ("*note %s: (%s)%s", pname, file, node);
  5123.     }
  5124. }
  5125.  
  5126. /* **************************************************************** */
  5127. /*                                                                  */
  5128. /*                      Insertion Command Stubs                     */
  5129. /*                                                                  */
  5130. /* **************************************************************** */
  5131.  
  5132. cm_quotation ()
  5133. {
  5134.   begin_insertion (quotation);
  5135. }
  5136.  
  5137. cm_example ()
  5138. {
  5139.   begin_insertion (example);
  5140. }
  5141.  
  5142. cm_smallexample ()
  5143. {
  5144.   begin_insertion (smallexample);
  5145. }
  5146.  
  5147. cm_lisp ()
  5148. {
  5149.   begin_insertion (lisp);
  5150. }
  5151.  
  5152. cm_smalllisp ()
  5153. {
  5154.   begin_insertion (smalllisp);
  5155. }
  5156.  
  5157. /* @cartouche/@end cartouche draws box with rounded corners in
  5158.    TeX output.  Right now, just a NOP insertion. */
  5159. cm_cartouche ()
  5160. {
  5161.   begin_insertion (cartouche);
  5162. }
  5163.  
  5164. cm_format ()
  5165. {
  5166.   begin_insertion (format);
  5167. }
  5168.  
  5169. cm_display ()
  5170. {
  5171.   begin_insertion (display);
  5172. }
  5173.  
  5174. cm_itemize ()
  5175. {
  5176.   begin_insertion (itemize);
  5177. }
  5178.  
  5179. cm_enumerate ()
  5180. {
  5181.   do_enumeration (enumerate, "1");
  5182. }
  5183.  
  5184. /* Start an enumeration insertion of type TYPE.  If the user supplied
  5185.    no argument on the line, then use DEFAULT_STRING as the initial string. */
  5186. do_enumeration (type, default_string)
  5187.      int type;
  5188.      char *default_string;
  5189. {
  5190.   get_until_in_line (".", &enumeration_arg);
  5191.   canon_white (enumeration_arg);
  5192.  
  5193.   if (!*enumeration_arg)
  5194.     {
  5195.       free (enumeration_arg);
  5196.       enumeration_arg = savestring (default_string);
  5197.     }
  5198.  
  5199.   if (!isdigit (*enumeration_arg) && !isletter (*enumeration_arg))
  5200.     {
  5201.       warning ("%s requires a letter or a digit", insertion_type_pname (type));
  5202.  
  5203.       switch (type)
  5204.         {
  5205.         case enumerate:
  5206.           default_string = "1";
  5207.           break;
  5208.         }
  5209.       enumeration_arg = savestring (default_string);
  5210.     }
  5211.   begin_insertion (type);
  5212. }
  5213.  
  5214. cm_table ()
  5215. {
  5216.   begin_insertion (table);
  5217. }
  5218.  
  5219. cm_ftable ()
  5220. {
  5221.   begin_insertion (ftable);
  5222. }
  5223.  
  5224. cm_vtable ()
  5225. {
  5226.   begin_insertion (vtable);
  5227. }
  5228.  
  5229. cm_group ()
  5230. {
  5231.   begin_insertion (group);
  5232. }
  5233.  
  5234. cm_ifinfo ()
  5235. {
  5236.   begin_insertion (ifinfo);
  5237. }
  5238.  
  5239. /* Begin an insertion where the lines are not filled or indented. */
  5240. cm_flushleft ()
  5241. {
  5242.   begin_insertion (flushleft);
  5243. }
  5244.  
  5245. /* Begin an insertion where the lines are not filled, and each line is
  5246.    forced to the right-hand side of the page. */
  5247. cm_flushright ()
  5248. {
  5249.   begin_insertion (flushright);
  5250. }
  5251.  
  5252.  
  5253. /* **************************************************************** */
  5254. /*                                                                  */
  5255. /*                        Conditional Handling                      */
  5256. /*                                                                  */
  5257. /* **************************************************************** */
  5258.  
  5259. /* A structure which contains `defined' variables. */
  5260. typedef struct _defines {
  5261.   struct _defines *next;
  5262.   char *name;
  5263.   char *value;
  5264. } DEFINE;
  5265.  
  5266. /* The linked list of `set' defines. */
  5267. DEFINE *defines = (DEFINE *)NULL;
  5268.  
  5269. /* Add NAME to the list of `set' defines. */
  5270. set (name, value)
  5271.      char *name;
  5272.      char *value;
  5273. {
  5274.   DEFINE *temp;
  5275.  
  5276.   for (temp = defines; temp; temp = temp->next)
  5277.     if (strcmp (name, temp->name) == 0)
  5278.       {
  5279.         free (temp->value);
  5280.         temp->value = savestring (value);
  5281.         return;
  5282.       }
  5283.  
  5284.   temp = (DEFINE *)xmalloc (sizeof (DEFINE));
  5285.   temp->next = defines;
  5286.   temp->name = savestring (name);
  5287.   temp->value = savestring (value);
  5288.   defines = temp;
  5289. }
  5290.  
  5291. /* Remove NAME from the list of `set' defines. */
  5292. clear (name)
  5293.      char *name;
  5294. {
  5295.   register DEFINE *temp, *last;
  5296.  
  5297.   last = (DEFINE *)NULL;
  5298.   temp = defines;
  5299.  
  5300.   while (temp)
  5301.     {
  5302.       if (strcmp (temp->name, name) == 0)
  5303.         {
  5304.           if (last)
  5305.             last->next = temp->next;
  5306.           else
  5307.             defines = temp->next;
  5308.  
  5309.           free (temp->name);
  5310.           free (temp->value);
  5311.           free (temp);
  5312.           break;
  5313.         }
  5314.       last = temp;
  5315.       temp = temp->next;
  5316.     }
  5317. }
  5318.  
  5319. /* Return the value of NAME.  The return value is NULL if NAME is unset. */
  5320. char *
  5321. set_p (name)
  5322.      char *name;
  5323. {
  5324.   register DEFINE *temp;
  5325.  
  5326.   for (temp = defines; temp; temp = temp->next)
  5327.     if (strcmp (temp->name, name) == 0)
  5328.       return (temp->value);
  5329.  
  5330.   return ((char *)NULL);
  5331. }
  5332.  
  5333. /* Conditionally parse based on the current command name. */
  5334. command_name_condition ()
  5335. {
  5336.   char *discarder;
  5337.  
  5338.   discarder = (char *)xmalloc (8 + strlen (command));
  5339.  
  5340.   sprintf (discarder, "\n@end %s", command);
  5341.   discard_until (discarder);
  5342.   discard_until ("\n");
  5343.  
  5344.   free (discarder);
  5345. }
  5346.  
  5347. /* Create a variable whose name appears as the first word on this line. */
  5348. cm_set ()
  5349. {
  5350.   handle_variable (SET);
  5351. }
  5352.  
  5353. /* Remove a variable whose name appears as the first word on this line. */
  5354. cm_clear ()
  5355. {
  5356.   handle_variable (CLEAR);
  5357. }
  5358.  
  5359. cm_ifset ()
  5360. {
  5361.   handle_variable (IFSET);
  5362. }
  5363.  
  5364. cm_ifclear ()
  5365. {
  5366.   handle_variable (IFCLEAR);
  5367. }
  5368.  
  5369. cm_value (arg, start_pos, end_pos)
  5370.      int arg, start_pos, end_pos;
  5371. {
  5372.   if (arg == END)
  5373.     {
  5374.       char *name, *value;
  5375.       name = (char *)&output_paragraph[start_pos];
  5376.       output_paragraph[end_pos] = '\0';
  5377.       name = savestring (name);
  5378.       value = set_p (name);
  5379.       output_paragraph_offset = start_pos;
  5380.  
  5381.       if (value)
  5382.         add_word_args ("%s", value);
  5383.       else
  5384.         add_word_args ("{No Value For \"%s\"}", name);
  5385.  
  5386.       free (name);
  5387.     }
  5388. }
  5389.  
  5390. /* Set, clear, or conditionalize based on ACTION. */
  5391. handle_variable (action)
  5392.      int action;
  5393. {
  5394.   char *name;
  5395.  
  5396.   get_rest_of_line (&name);
  5397.   backup_input_pointer ();
  5398.   canon_white (name);
  5399.   handle_variable_internal (action, name);
  5400.   free (name);
  5401. }
  5402.  
  5403. handle_variable_internal (action, name)
  5404.      int action;
  5405.      char *name;
  5406. {
  5407.   char *temp;
  5408.   int delimiter, additional_text_present = 0;
  5409.  
  5410.   /* Only the first word of NAME is a valid tag. */
  5411.   temp = name;
  5412.   delimiter = 0;
  5413.   while (*temp && (delimiter || !whitespace (*temp)))
  5414.     {
  5415. #if defined (SET_WITH_EQUAL)
  5416.       if (*temp == '"' || *temp == '\'')
  5417.         {
  5418.           if (*temp == delimiter)
  5419.             delimiter = 0;
  5420.           else
  5421.             delimiter = *temp;
  5422.         }
  5423. #endif /* SET_WITH_EQUAL */
  5424.       temp++;
  5425.     }
  5426.  
  5427.   if (*temp)
  5428.     additional_text_present++;
  5429.  
  5430.   *temp = '\0';
  5431.  
  5432.   if (!*name)
  5433.     line_error ("@%s requires a name", command);
  5434.   else
  5435.     {
  5436.       switch (action)
  5437.         {
  5438.         case SET:
  5439.           {
  5440.             char *value;
  5441.  
  5442. #if defined (SET_WITH_EQUAL)
  5443.             /* Allow a value to be saved along with a variable.  The value is
  5444.                the text following an `=' sign in NAME, if any is present. */
  5445.  
  5446.             for (value = name; *value && *value != '='; value++);
  5447.  
  5448.             if (*value)
  5449.               *value++ = '\0';
  5450.  
  5451.             if (*value == '"' || *value == '\'')
  5452.               {
  5453.                 value++;
  5454.                 value[strlen (value) - 1] = '\0';
  5455.               }
  5456. #else /* !SET_WITH_EQUAL */
  5457.             /* The VALUE of NAME is the remainder of the line sans
  5458.                whitespace. */
  5459.             if (additional_text_present)
  5460.               {
  5461.                 value = temp + 1;
  5462.                 canon_white (value);
  5463.               }
  5464.             else
  5465.               value = "";
  5466. #endif /* !SET_WITH_VALUE */
  5467.  
  5468.  
  5469.             set (name, value);
  5470.           }
  5471.           break;
  5472.  
  5473.         case CLEAR:
  5474.           clear (name);
  5475.           break;
  5476.  
  5477.         case IFSET:
  5478.         case IFCLEAR:
  5479.           /* If IFSET and NAME is not set, or if IFCLEAR and NAME is set,
  5480.              read lines from the the file until we reach a matching
  5481.              "@end CONDITION".  This means that we only take note of
  5482.              "@ifset/clear" and "@end" commands. */
  5483.           {
  5484.             char condition[8];
  5485.             int condition_len;
  5486.  
  5487.             if (action == IFSET)
  5488.               strcpy (condition, "ifset");
  5489.             else
  5490.               strcpy (condition, "ifclear");
  5491.  
  5492.             condition_len = strlen (condition);
  5493.  
  5494.           if ((action == IFSET && !set_p (name)) ||
  5495.               (action == IFCLEAR && set_p (name)))
  5496.             {
  5497.               int level = 0, done = 0;
  5498.  
  5499.               while (!done)
  5500.                 {
  5501.                   char *freeable_line, *line;
  5502.  
  5503.                   get_rest_of_line (&freeable_line);
  5504.  
  5505.                   for (line = freeable_line; whitespace (*line); line++);
  5506.  
  5507.                   if (*line == COMMAND_PREFIX &&
  5508.                       (strncmp (line + 1, condition, condition_len) == 0))
  5509.                     level++;
  5510.                   else if (strncmp (line, "@end", 4) == 0)
  5511.                     {
  5512.                       char *cname = line + 4;
  5513.                       char *temp;
  5514.  
  5515.                       while (*cname && whitespace (*cname))
  5516.                         cname++;
  5517.                       temp = cname;
  5518.  
  5519.                       while (*temp && !whitespace (*temp))
  5520.                         temp++;
  5521.                       *temp = '\0';
  5522.  
  5523.                       if (strcmp (cname, condition) == 0)
  5524.                         {
  5525.                           if (!level)
  5526.                             {
  5527.                               done = 1;
  5528.                             }
  5529.                           else
  5530.                             level--;
  5531.                         }
  5532.                     }
  5533.                   free (freeable_line);
  5534.                 }
  5535.               break;
  5536.             }
  5537.           else
  5538.             {
  5539.               if (action == IFSET)
  5540.                 begin_insertion (ifset);
  5541.               else
  5542.                 begin_insertion (ifclear);
  5543.             }
  5544.           }
  5545.           break;
  5546.         }
  5547.     }
  5548. }
  5549.  
  5550. /* **************************************************************** */
  5551. /*                                                                  */
  5552. /*                      @itemx, @item                               */
  5553. /*                                                                  */
  5554. /* **************************************************************** */
  5555.  
  5556. /* Non-zero means a string is in execution, as opposed to a file. */
  5557. int executing_string = 0;
  5558.  
  5559. /* Execute the string produced by formatting the ARGs with FORMAT.  This
  5560.    is like submitting a new file with @include. */
  5561. execute_string (format, arg1, arg2, arg3, arg4, arg5)
  5562.      char *format;
  5563. {
  5564.   static char temp_string[4000];
  5565.   sprintf (temp_string, format, arg1, arg2, arg3, arg4, arg5);
  5566.   strcat (temp_string, "@bye\n");
  5567.   pushfile ();
  5568.   input_text_offset = 0;
  5569.   input_text = temp_string;
  5570.   input_filename = savestring (input_filename);
  5571.   size_of_input_text = strlen (temp_string);
  5572.  
  5573.   executing_string++;
  5574.   reader_loop ();
  5575.  
  5576.   popfile ();
  5577.   executing_string--;
  5578.  
  5579.   free_and_clear (&command);
  5580.   command = savestring ("not bye");
  5581. }
  5582.  
  5583. int itemx_flag = 0;
  5584.  
  5585. cm_itemx ()
  5586. {
  5587.   itemx_flag++;
  5588.   cm_item ();
  5589.   itemx_flag--;
  5590. }
  5591.  
  5592. cm_item ()
  5593. {
  5594.   char *rest_of_line, *item_func;
  5595.  
  5596.   /* Can only hack "@item" while inside of an insertion. */
  5597.   if (insertion_level)
  5598.     {
  5599.       INSERTION_ELT *stack = insertion_stack;
  5600.       int original_input_text_offset;
  5601.  
  5602.       skip_whitespace ();
  5603.       original_input_text_offset = input_text_offset;
  5604.  
  5605.       get_rest_of_line (&rest_of_line);
  5606.       canon_white (rest_of_line);
  5607.       item_func = current_item_function ();
  5608.  
  5609.       /* Okay, do the right thing depending on which insertion function
  5610.          is active. */
  5611.  
  5612.     switch_top:
  5613.       switch (stack->insertion)
  5614.         {
  5615.         case ifinfo:
  5616.         case ifset:
  5617.         case ifclear:
  5618.         case cartouche:
  5619.           stack = stack->next;
  5620.           if (!stack)
  5621.             goto no_insertion;
  5622.           else
  5623.             goto switch_top;
  5624.           break;
  5625.  
  5626.         case menu:
  5627.         case quotation:
  5628.         case example:
  5629.         case smallexample:
  5630.         case lisp:
  5631.         case format:
  5632.         case display:
  5633.         case group:
  5634.           line_error ("The `@%s' command is meaningless within a `@%s' block",
  5635.                       command,
  5636.                       insertion_type_pname (current_insertion_type ()));
  5637.           break;
  5638.  
  5639.         case itemize:
  5640.         case enumerate:
  5641.           if (itemx_flag)
  5642.             {
  5643.               line_error ("@itemx is not meaningful inside of a `%s' block",
  5644.                           insertion_type_pname (current_insertion_type ()));
  5645.             }
  5646.           else
  5647.             {
  5648.               start_paragraph ();
  5649.               kill_self_indent (-1);
  5650.               filling_enabled = indented_fill = 1;
  5651.  
  5652.               if (current_insertion_type () == itemize)
  5653.                 {
  5654.                   indent (output_column = current_indent - 2);
  5655.  
  5656.                   /* I need some way to determine whether this command
  5657.                      takes braces or not.  I believe the user can type
  5658.                      either "@bullet" or "@bullet{}".  Of course, they
  5659.                      can also type "o" or "#" or whatever else they want. */
  5660.                   if (item_func && *item_func)
  5661.                     {
  5662.                       if (*item_func == '@')
  5663.                         if (item_func[strlen (item_func) - 1] != '}')
  5664.                           execute_string ("%s{}", item_func);
  5665.                         else
  5666.                           execute_string ("%s", item_func);
  5667.                       else
  5668.                         execute_string ("%s", item_func);
  5669.                     }
  5670.                   insert (' ');
  5671.                   output_column++;
  5672.                 }
  5673.               else
  5674.                 enumerate_item ();
  5675.  
  5676.               /* Special hack.  This makes close paragraph ignore you until
  5677.                  the start_paragraph () function has been called. */
  5678.               must_start_paragraph = 1;
  5679.  
  5680.               /* Ultra special hack.  It appears that some people incorrectly
  5681.                  place text directly after the @item, instead of on a new line
  5682.                  by itself.  This happens to work in TeX, so I make it work
  5683.                  here. */
  5684.               if (*rest_of_line)
  5685.                 {
  5686.                   line_number--;
  5687.                   input_text_offset = original_input_text_offset;
  5688.                 }
  5689.             }
  5690.           break;
  5691.  
  5692.         case table:
  5693.         case ftable:
  5694.         case vtable:
  5695.           {
  5696.             /* Get rid of extra characters. */
  5697.             kill_self_indent (-1);
  5698.  
  5699.             /* close_paragraph () almost does what we want.  The problem
  5700.                is when paragraph_is_open, and last_char_was_newline, and
  5701.                the last newline has been turned into a space, because
  5702.                filling_enabled. I handle it here. */
  5703.             if (last_char_was_newline && filling_enabled && paragraph_is_open)
  5704.               insert ('\n');
  5705.             close_paragraph ();
  5706.  
  5707. #if defined (INDENT_PARAGRAPHS_IN_TABLE)
  5708.             /* Indent on a new line, but back up one indentation level. */
  5709.             {
  5710.               int t;
  5711.  
  5712.               t = inhibit_paragraph_indentation;
  5713.               inhibit_paragraph_indentation = 1;
  5714.               /* At this point, inserting any non-whitespace character will
  5715.                  force the existing indentation to be output. */
  5716.               add_char ('i');
  5717.               inhibit_paragraph_indentation = t;
  5718.             }
  5719. #else /* !INDENT_PARAGRAPHS_IN_TABLE */
  5720.             add_char ('i');
  5721. #endif /* !INDENT_PARAGRAPHS_IN_TABLE */
  5722.  
  5723.             output_paragraph_offset--;
  5724.             kill_self_indent (default_indentation_increment + 1);
  5725.  
  5726.             /* Add item's argument to the line. */
  5727.             filling_enabled = 0;
  5728.             if (!item_func && !(*item_func))
  5729.               execute_string ("%s", rest_of_line);
  5730.             else
  5731.               execute_string ("%s{%s}", item_func, rest_of_line);
  5732.  
  5733.             if (current_insertion_type () == ftable)
  5734.               execute_string ("@findex %s\n", rest_of_line);
  5735.  
  5736.             if (current_insertion_type () == vtable)
  5737.               execute_string ("@vindex %s\n", rest_of_line);
  5738.  
  5739.             /* Start a new line, and let start_paragraph ()
  5740.                do the indenting of it for you. */
  5741.             close_single_paragraph ();
  5742.             indented_fill = filling_enabled = 1;
  5743.           }
  5744.         }
  5745.       free (rest_of_line);
  5746.     }
  5747.   else
  5748.     {
  5749.     no_insertion:
  5750.       line_error ("@%s found outside of an insertion block", command);
  5751.     }
  5752. }
  5753.  
  5754.  
  5755. /* **************************************************************** */
  5756. /*                                                                  */
  5757. /*                      Defun and Friends                           */
  5758. /*                                                                  */
  5759. /* **************************************************************** */
  5760.  
  5761. #define DEFUN_SELF_DELIMITING(c)                                        \
  5762.   (((c) == '(')                                                         \
  5763.    || ((c) == ')')                                                      \
  5764.    || ((c) == '[')                                                      \
  5765.    || ((c) == ']'))
  5766.  
  5767. struct token_accumulator
  5768. {
  5769.   unsigned int length;
  5770.   unsigned int index;
  5771.   char **tokens;
  5772. };
  5773.  
  5774. void
  5775. initialize_token_accumulator (accumulator)
  5776.      struct token_accumulator *accumulator;
  5777. {
  5778.   (accumulator->length) = 0;
  5779.   (accumulator->index) = 0;
  5780.   (accumulator->tokens) = NULL;
  5781. }
  5782.  
  5783. void
  5784. accumulate_token (accumulator, token)
  5785.      struct token_accumulator *accumulator;
  5786.      char *token;
  5787. {
  5788.   if ((accumulator->index) >= (accumulator->length))
  5789.     {
  5790.       (accumulator->length) += 10;
  5791.       (accumulator->tokens) = (char **) xrealloc
  5792.         (accumulator->tokens, (accumulator->length * sizeof (char *)));
  5793.     }
  5794.   accumulator->tokens[accumulator->index] = token;
  5795.   accumulator->index += 1;
  5796. }
  5797.  
  5798. char *
  5799. copy_substring (start, end)
  5800.      char *start;
  5801.      char *end;
  5802. {
  5803.   char *result, *scan, *scan_result;
  5804.  
  5805.   result = (char *) xmalloc ((end - start) + 1);
  5806.   scan_result = result;
  5807.   scan = start;
  5808.  
  5809.   while (scan < end)
  5810.     *scan_result++ = *scan++;
  5811.  
  5812.   *scan_result = '\0';
  5813.   return (result);
  5814. }
  5815.  
  5816. /* Given `string' pointing at an open brace, skip forward and return a
  5817.    pointer to just past the matching close brace. */
  5818. int
  5819. scan_group_in_string (string_pointer)
  5820.      char **string_pointer;
  5821. {
  5822.   register int c;
  5823.   register char *scan_string;
  5824.   register unsigned int level = 1;
  5825.  
  5826.   scan_string = (*string_pointer) + 1;
  5827.  
  5828.   while (1)
  5829.     {
  5830.       if (level == 0)
  5831.         {
  5832.           (*string_pointer) = scan_string;
  5833.           return (1);
  5834.         }
  5835.       c = (*scan_string++);
  5836.       if (c == '\0')
  5837.         {
  5838.           /* Tweak line_number to compensate for fact that
  5839.              we gobbled the whole line before coming here. */
  5840.           line_number -= 1;
  5841.           line_error ("Missing `}' in @def arg");
  5842.           line_number += 1;
  5843.           (*string_pointer) = (scan_string - 1);
  5844.           return (0);
  5845.         }
  5846.       if (c == '{')
  5847.         level += 1;
  5848.       if (c == '}')
  5849.         level -= 1;
  5850.     }
  5851. }
  5852.  
  5853. /* Return a list of tokens from the contents of `string'.
  5854.    Commands and brace-delimited groups count as single tokens.
  5855.    Contiguous whitespace characters are converted to a token
  5856.    consisting of a single space. */
  5857. char **
  5858. args_from_string (string)
  5859.      char *string;
  5860. {
  5861.   struct token_accumulator accumulator;
  5862.   register char *scan_string = string;
  5863.   char *token_start, *token_end;
  5864.  
  5865.   initialize_token_accumulator (&accumulator);
  5866.  
  5867.   while ((*scan_string) != '\0')
  5868.     {
  5869.       /* Replace arbitrary whitespace by a single space. */
  5870.       if (whitespace (*scan_string))
  5871.         {
  5872.           scan_string += 1;
  5873.           while (whitespace (*scan_string))
  5874.             scan_string += 1;
  5875.           accumulate_token ((&accumulator), (savestring (" ")));
  5876.           continue;
  5877.         }
  5878.  
  5879.       /* Commands count as single tokens. */
  5880.       if ((*scan_string) == COMMAND_PREFIX)
  5881.         {
  5882.           token_start = scan_string;
  5883.           scan_string += 1;
  5884.           if (self_delimiting (*scan_string))
  5885.             scan_string += 1;
  5886.           else
  5887.             {
  5888.               register int c;
  5889.               while (1)
  5890.                 {
  5891.                   c = *scan_string++;
  5892.  
  5893.                   if ((c == '\0') || (c == '{') || (whitespace (c)))
  5894.                     {
  5895.                       scan_string -= 1;
  5896.                       break;
  5897.                     }
  5898.                 }
  5899.  
  5900.               if (*scan_string == '{')
  5901.                 {
  5902.                   char *s = scan_string;
  5903.                   (void) scan_group_in_string (&s);
  5904.                   scan_string = s;
  5905.                 }
  5906.             }
  5907.           token_end = scan_string;
  5908.         }
  5909.  
  5910.       /* Parentheses and brackets are self-delimiting. */
  5911.       else if (DEFUN_SELF_DELIMITING (*scan_string))
  5912.         {
  5913.           token_start = scan_string;
  5914.           scan_string += 1;
  5915.           token_end = scan_string;
  5916.         }
  5917.  
  5918.       /* Open brace introduces a group that is a single token. */
  5919.       else if (*scan_string == '{')
  5920.         {
  5921.           char *s = scan_string;
  5922.           int balanced = scan_group_in_string (&s);
  5923.  
  5924.           token_start = scan_string + 1;
  5925.           scan_string = s;
  5926.           token_end = balanced ? (scan_string - 1) : scan_string;
  5927.         }
  5928.  
  5929.       /* Otherwise a token is delimited by whitespace, parentheses,
  5930.          brackets, or braces.  A token is also ended by a command. */
  5931.       else
  5932.         {
  5933.           token_start = scan_string;
  5934.  
  5935.           while (1)
  5936.             {
  5937.               register int c;
  5938.  
  5939.               c = *scan_string++;
  5940.  
  5941.               if (!c ||
  5942.                   (whitespace (c) || DEFUN_SELF_DELIMITING (c) ||
  5943.                    c == '{' || c == '}'))
  5944.                 {
  5945.                   scan_string--;
  5946.                   break;
  5947.                 }
  5948.  
  5949.               /* If we encounter a command imbedded within a token,
  5950.                  then end the token. */
  5951.               if (c == COMMAND_PREFIX)
  5952.                 {
  5953.                   scan_string--;
  5954.                   break;
  5955.                 }
  5956.             }
  5957.           token_end = scan_string;
  5958.         }
  5959.  
  5960.       accumulate_token
  5961.         (&accumulator, copy_substring (token_start, token_end));
  5962.     }
  5963.   accumulate_token (&accumulator, NULL);
  5964.   return (accumulator.tokens);
  5965. }
  5966.  
  5967. void
  5968. process_defun_args (defun_args, auto_var_p)
  5969.      char **defun_args;
  5970.      int auto_var_p;
  5971. {
  5972.   int pending_space = 0;
  5973.  
  5974.   while (1)
  5975.     {
  5976.       char *defun_arg = *defun_args++;
  5977.  
  5978.       if (defun_arg == NULL)
  5979.         break;
  5980.  
  5981.       if (defun_arg[0] == ' ')
  5982.         {
  5983.           pending_space = 1;
  5984.           continue;
  5985.         }
  5986.  
  5987.       if (pending_space)
  5988.         {
  5989.           add_char (' ');
  5990.           pending_space = 0;
  5991.         }
  5992.  
  5993.       if (DEFUN_SELF_DELIMITING (defun_arg[0]))
  5994.         add_char (defun_arg[0]);
  5995.       else if (defun_arg[0] == '&')
  5996.         add_word (defun_arg);
  5997.       else if (defun_arg[0] == COMMAND_PREFIX)
  5998.         execute_string ("%s", defun_arg);
  5999.       else if (auto_var_p)
  6000.         execute_string ("@var{%s}", defun_arg);
  6001.       else
  6002.         add_word (defun_arg);
  6003.     }
  6004. }
  6005.  
  6006. char *
  6007. next_nonwhite_defun_arg (arg_pointer)
  6008.      char ***arg_pointer;
  6009. {
  6010.   char **scan = (*arg_pointer);
  6011.   char *arg = (*scan++);
  6012.  
  6013.   if ((arg != 0) && (*arg == ' '))
  6014.     arg = *scan++;
  6015.  
  6016.   if (arg == 0)
  6017.     scan -= 1;
  6018.  
  6019.   *arg_pointer = scan;
  6020.  
  6021.   return ((arg == 0) ? "" : arg);
  6022. }
  6023.  
  6024. /* Make the defun type insertion.
  6025.    TYPE says which insertion this is.
  6026.    X_P says not to start a new insertion if non-zero. */
  6027. void
  6028. defun_internal (type, x_p)
  6029.      enum insertion_type type;
  6030.      int x_p;
  6031. {
  6032.   enum insertion_type base_type;
  6033.   char **defun_args, **scan_args;
  6034.   char *category, *defined_name, *type_name, *type_name2;
  6035.  
  6036.   {
  6037.     char *line;
  6038.     get_rest_of_line (&line);
  6039.     defun_args = (args_from_string (line));
  6040.     free (line);
  6041.   }
  6042.  
  6043.   scan_args = defun_args;
  6044.  
  6045.   switch (type)
  6046.     {
  6047.     case defun:
  6048.       category = "Function";
  6049.       base_type = deffn;
  6050.       break;
  6051.     case defmac:
  6052.       category = "Macro";
  6053.       base_type = deffn;
  6054.       break;
  6055.     case defspec:
  6056.       category = "Special Form";
  6057.       base_type = deffn;
  6058.       break;
  6059.     case defvar:
  6060.       category = "Variable";
  6061.       base_type = defvr;
  6062.       break;
  6063.     case defopt:
  6064.       category = "User Option";
  6065.       base_type = defvr;
  6066.       break;
  6067.     case deftypefun:
  6068.       category = "Function";
  6069.       base_type = deftypefn;
  6070.       break;
  6071.     case deftypevar:
  6072.       category = "Variable";
  6073.       base_type = deftypevr;
  6074.       break;
  6075.     case defivar:
  6076.       category = "Instance Variable";
  6077.       base_type = defcv;
  6078.       break;
  6079.     case defmethod:
  6080.       category = "Method";
  6081.       base_type = defop;
  6082.       break;
  6083.     case deftypemethod:
  6084.       category = "Method";
  6085.       base_type = deftypemethod;
  6086.       break;
  6087.     default:
  6088.       category = next_nonwhite_defun_arg (&scan_args);
  6089.       base_type = type;
  6090.       break;
  6091.     }
  6092.  
  6093.   if ((base_type == deftypefn)
  6094.       || (base_type == deftypevr)
  6095.       || (base_type == defcv)
  6096.       || (base_type == defop)
  6097.       || (base_type == deftypemethod))
  6098.     type_name = next_nonwhite_defun_arg (&scan_args);
  6099.  
  6100.   if (base_type == deftypemethod)
  6101.     type_name2 = next_nonwhite_defun_arg (&scan_args);
  6102.  
  6103.   defined_name = next_nonwhite_defun_arg (&scan_args);
  6104.  
  6105.   /* This hack exists solely for the purposes of formatting the texinfo
  6106.      manual.  I couldn't think of a better way.  The token might be
  6107.      a simple @@ followed immediately by more text.  If this is the case,
  6108.      then the next defun arg is part of this one, and we should concatenate
  6109.      them. */
  6110.   if (*scan_args && **scan_args && !whitespace (**scan_args) &&
  6111.       (strcmp (defined_name, "@@") == 0))
  6112.     {
  6113.       char *tem = (char *)xmalloc (3 + strlen (scan_args[0]));
  6114.  
  6115.       sprintf (tem, "@@%s", scan_args[0]);
  6116.  
  6117.       free (scan_args[0]);
  6118.       scan_args[0] = tem;
  6119.       scan_args++;
  6120.       defined_name = tem;
  6121.     }
  6122.  
  6123.   if (!x_p)
  6124.     begin_insertion (type);
  6125.  
  6126.   /* Write the definition header line.
  6127.      This should start at the normal indentation.  */
  6128.   current_indent -= default_indentation_increment;
  6129.   start_paragraph ();
  6130.  
  6131.   switch (base_type)
  6132.     {
  6133.     case deffn:
  6134.     case defvr:
  6135.     case deftp:
  6136.       execute_string (" -- %s: %s", category, defined_name);
  6137.       break;
  6138.     case deftypefn:
  6139.     case deftypevr:
  6140.       execute_string (" -- %s: %s %s", category, type_name, defined_name);
  6141.       break;
  6142.     case defcv:
  6143.       execute_string (" -- %s of %s: %s", category, type_name, defined_name);
  6144.       break;
  6145.     case defop:
  6146.       execute_string (" -- %s on %s: %s", category, type_name, defined_name);
  6147.       break;
  6148.     case deftypemethod:
  6149.       execute_string (" -- %s on %s: %s %s", category, type_name, type_name2,
  6150.                       defined_name);
  6151.       break;
  6152.     }
  6153.   current_indent += default_indentation_increment;
  6154.  
  6155.   /* Now process the function arguments, if any.
  6156.      If these carry onto the next line, they should be indented by two
  6157.      increments to distinguish them from the body of the definition,
  6158.      which is indented by one increment.  */
  6159.   current_indent += default_indentation_increment;
  6160.  
  6161.   switch (base_type)
  6162.     {
  6163.     case deffn:
  6164.     case defop:
  6165.       process_defun_args (scan_args, 1);
  6166.       break;
  6167.     case deftp:
  6168.     case deftypefn:
  6169.     case deftypemethod:
  6170.       process_defun_args (scan_args, 0);
  6171.       break;
  6172.     }
  6173.   current_indent -= default_indentation_increment;
  6174.   close_single_paragraph ();
  6175.  
  6176.   /* Make an entry in the appropriate index. */
  6177.   switch (base_type)
  6178.     {
  6179.     case deffn:
  6180.     case deftypefn:
  6181.       execute_string ("@findex %s\n", defined_name);
  6182.       break;
  6183.     case defvr:
  6184.     case deftypevr:
  6185.     case defcv:
  6186.       execute_string ("@vindex %s\n", defined_name);
  6187.       break;
  6188.     case defop:
  6189.     case deftypemethod:
  6190.       execute_string ("@findex %s on %s\n", defined_name, type_name);
  6191.       break;
  6192.     case deftp:
  6193.       execute_string ("@tindex %s\n", defined_name);
  6194.       break;
  6195.     }
  6196.  
  6197.   /* Deallocate the token list. */
  6198.   scan_args = defun_args;
  6199.   while (1)
  6200.     {
  6201.       char * arg = (*scan_args++);
  6202.       if (arg == NULL)
  6203.         break;
  6204.       free (arg);
  6205.     }
  6206.   free (defun_args);
  6207. }
  6208.  
  6209. /* Add an entry for a function, macro, special form, variable, or option.
  6210.    If the name of the calling command ends in `x', then this is an extra
  6211.    entry included in the body of an insertion of the same type. */
  6212. cm_defun ()
  6213. {
  6214.   int x_p;
  6215.   enum insertion_type type;
  6216.   char *temp = savestring (command);
  6217.  
  6218.   x_p = (command[strlen (command) - 1] == 'x');
  6219.  
  6220.   if (x_p)
  6221.     temp[strlen (temp) - 1] = '\0';
  6222.  
  6223.   type = find_type_from_name (temp);
  6224.   free (temp);
  6225.  
  6226.   /* If we are adding to an already existing insertion, then make sure
  6227.      that we are already in an insertion of type TYPE. */
  6228.   if (x_p &&
  6229.       (!insertion_level || insertion_stack->insertion != type))
  6230.     {
  6231.       line_error ("Must be in a `%s' insertion in order to use `%s'x",
  6232.                   command, command);
  6233.       discard_until ("\n");
  6234.       return;
  6235.     }
  6236.  
  6237.   defun_internal (type, x_p);
  6238. }
  6239.  
  6240. /* End existing insertion block. */
  6241. cm_end ()
  6242. {
  6243.   char *temp;
  6244.   enum insertion_type type;
  6245.  
  6246.   if (!insertion_level)
  6247.     {
  6248.       line_error ("Unmatched `@%s'", command);
  6249.       return;
  6250.     }
  6251.  
  6252.   get_rest_of_line (&temp);
  6253.   canon_white (temp);
  6254.  
  6255.   if (strlen (temp) == 0)
  6256.     line_error ("`@%s' needs something after it", command);
  6257.  
  6258.   type = find_type_from_name (temp);
  6259.  
  6260.   if (type == bad_type)
  6261.     {
  6262.       line_error ("Bad argument to `%s', `%s', using `%s'",
  6263.            command, temp, insertion_type_pname (current_insertion_type ()));
  6264.     }
  6265.   end_insertion (type);
  6266.   free (temp);
  6267. }
  6268.  
  6269.  
  6270. /* **************************************************************** */
  6271. /*                                                                  */
  6272. /*                      Other Random Commands                       */
  6273. /*                                                                  */
  6274. /* **************************************************************** */
  6275.  
  6276. /* This says to inhibit the indentation of the next paragraph, but
  6277.    not of following paragraphs.  */
  6278. cm_noindent ()
  6279. {
  6280.   if (!inhibit_paragraph_indentation)
  6281.     inhibit_paragraph_indentation = -1;
  6282. }
  6283.  
  6284. /* I don't know exactly what to do with this.  Should I allow
  6285.    someone to switch filenames in the middle of output?  Since the
  6286.    file could be partially written, this doesn't seem to make sense.
  6287.    Another option: ignore it, since they don't *really* want to
  6288.    switch files.  Finally, complain, or at least warn. */
  6289. cm_setfilename ()
  6290. {
  6291.   char *filename;
  6292.   get_rest_of_line (&filename);
  6293.   /* warning ("`@%s %s' encountered and ignored", command, filename); */
  6294.   free (filename);
  6295. }
  6296.  
  6297. cm_ignore_line ()
  6298. {
  6299.   discard_until ("\n");
  6300. }
  6301.  
  6302. /* @br can be immediately followed by `{}', so we have to read those here.
  6303.    It should simply close the paragraph. */
  6304. cm_br ()
  6305. {
  6306.   if (looking_at ("{}"))
  6307.     input_text_offset += 2;
  6308.  
  6309.   if (curchar () == '\n')
  6310.     {
  6311.       input_text_offset++;
  6312.       line_number++;
  6313.     }
  6314.  
  6315.   close_paragraph ();
  6316. }
  6317.  
  6318.  /* Insert the number of blank lines passed as argument. */
  6319. cm_sp ()
  6320. {
  6321.   int lines;
  6322.   char *line;
  6323.  
  6324.   get_rest_of_line (&line);
  6325.  
  6326.   if (sscanf (line, "%d", &lines) != 1)
  6327.     {
  6328.       line_error ("%csp requires a positive numeric argument", COMMAND_PREFIX);
  6329.     }
  6330.   else
  6331.     {
  6332.       if (lines < 0)
  6333.     lines = 0;
  6334.  
  6335.       while (lines--)
  6336.     add_char ('\n');
  6337.     }
  6338.   free (line);
  6339. }
  6340.  
  6341. /* Start a new line with just this text on it.
  6342.    Then center the line of text.
  6343.    This always ends the current paragraph. */
  6344. cm_center ()
  6345. {
  6346.   register int i, start, length;
  6347.   int fudge_factor = 1;
  6348.   unsigned char *line;
  6349.  
  6350.   close_paragraph ();
  6351.   filling_enabled = indented_fill = 0;
  6352.   cm_noindent ();
  6353.   start = output_paragraph_offset;
  6354.   inhibit_output_flushing ();
  6355.   get_rest_of_line ((char **)&line);
  6356.   execute_string ((char *)line);
  6357.   free (line);
  6358.   uninhibit_output_flushing ();
  6359.  
  6360.   i = output_paragraph_offset - 1;
  6361.   while (i > (start - 1) && output_paragraph[i] == '\n')
  6362.         i--;
  6363.  
  6364.   output_paragraph_offset = ++i;
  6365.   length = output_paragraph_offset - start;
  6366.  
  6367.   if (length < (fill_column - fudge_factor))
  6368.     {
  6369.       line = (unsigned char *)xmalloc (1 + length);
  6370.       bcopy (output_paragraph + start, line, length);
  6371.  
  6372.       i = (fill_column - fudge_factor - length) / 2;
  6373.       output_paragraph_offset = start;
  6374.  
  6375.       while (i--)
  6376.         insert (' ');
  6377.  
  6378.       for (i = 0; i < length; i++)
  6379.         insert (line[i]);
  6380.  
  6381.       free (line);
  6382.     }
  6383.  
  6384.   insert ('\n');
  6385.   close_paragraph ();
  6386.   filling_enabled = 1;
  6387. }
  6388.  
  6389. /* Show what an expression returns. */
  6390. cm_result (arg)
  6391.      int arg;
  6392. {
  6393.   if (arg == END)
  6394.     add_word ("=>");
  6395. }
  6396.  
  6397. /* What an expression expands to. */
  6398. cm_expansion (arg)
  6399.      int arg;
  6400. {
  6401.   if (arg == END)
  6402.     add_word ("==>");
  6403. }
  6404.  
  6405. /* Indicates two expressions are equivalent. */
  6406. cm_equiv (arg)
  6407.      int arg;
  6408. {
  6409.   if (arg == END)
  6410.     add_word ("==");
  6411. }
  6412.  
  6413. /* What an expression may print. */
  6414. cm_print (arg)
  6415.      int arg;
  6416. {
  6417.   if (arg == END)
  6418.     add_word ("-|");
  6419. }
  6420.  
  6421. /* An error signaled. */
  6422. cm_error (arg)
  6423.      int arg;
  6424. {
  6425.   if (arg == END)
  6426.     add_word ("error-->");
  6427. }
  6428.  
  6429. /* The location of point in an example of a buffer. */
  6430. cm_point (arg)
  6431.      int arg;
  6432. {
  6433.   if (arg == END)
  6434.     add_word ("-!-");
  6435. }
  6436.  
  6437. /* Start a new line with just this text on it.
  6438.    The text is outdented one level if possible. */
  6439. cm_exdent ()
  6440. {
  6441.   char *line;
  6442.   int i = current_indent;
  6443.  
  6444.   if (current_indent)
  6445.     current_indent -= default_indentation_increment;
  6446.  
  6447.   get_rest_of_line (&line);
  6448.   close_single_paragraph ();
  6449.   execute_string (line);
  6450.   current_indent = i;
  6451.   free (line);
  6452.   close_single_paragraph ();
  6453. }
  6454.  
  6455. cm_include ()
  6456. {
  6457.   cm_infoinclude ();
  6458. }
  6459.  
  6460. /* Remember this file, and move onto the next. */
  6461. cm_infoinclude ()
  6462. {
  6463.   char *filename;
  6464.  
  6465.   close_paragraph ();
  6466.   get_rest_of_line (&filename);
  6467.   pushfile ();
  6468.  
  6469.   /* In verbose mode we print info about including another file. */
  6470.   if (verbose_mode)
  6471.     {
  6472.       register int i = 0;
  6473.       register FSTACK *stack = filestack;
  6474.  
  6475.       for (i = 0, stack = filestack; stack; stack = stack->next, i++);
  6476.  
  6477.       i *= 2;
  6478.  
  6479.       printf ("%*s", i, "");
  6480.       printf ("%c%s %s\n", COMMAND_PREFIX, command, filename);
  6481.       fflush (stdout);
  6482.     }
  6483.  
  6484.   if (!find_and_load (filename))
  6485.     {
  6486.       extern char *sys_errlist[];
  6487.       extern int errno, sys_nerr;
  6488.       popfile ();
  6489.  
  6490.       /* Cannot "@include foo", in line 5 of "/wh/bar". */
  6491.       line_error ("`%c%s %s': %s", COMMAND_PREFIX, command, filename,
  6492.                   ((errno < sys_nerr) ?
  6493.                    sys_errlist[errno] : "Unknown file system error"));
  6494.     }
  6495.   free (filename);
  6496. }
  6497.  
  6498. /* The other side of a malformed expression. */
  6499. misplaced_brace ()
  6500. {
  6501.   line_error ("Misplaced `}'");
  6502. }
  6503.  
  6504. /* Don't let the filling algorithm insert extra whitespace here. */
  6505. cm_force_abbreviated_whitespace ()
  6506. {
  6507. }
  6508.  
  6509. /* Do not let this character signify the end of a sentence, though
  6510.    if it was seen without the command prefix it normally would.  We
  6511.    do this by turning on the 8th bit of the character. */
  6512. cm_ignore_sentence_ender ()
  6513. {
  6514.   if (amiga_guide) add_char (META ((*command - 32)));
  6515.   else add_char (META ((*command)));
  6516. }
  6517.  
  6518. /* Signals end of processing.  Easy to make this happen. */
  6519. cm_bye ()
  6520. {
  6521.   input_text_offset = size_of_input_text;
  6522. }
  6523.  
  6524. cm_asis ()
  6525. {
  6526. }
  6527.  
  6528. cm_math ()
  6529. {
  6530. }
  6531.  
  6532.  
  6533. /* **************************************************************** */
  6534. /*                                                                  */
  6535. /*                      Indexing Stuff                              */
  6536. /*                                                                  */
  6537. /* **************************************************************** */
  6538.  
  6539.  
  6540. /* An index element... */
  6541. typedef struct index_elt
  6542. {
  6543.   struct index_elt *next;
  6544.   char *entry;                  /* The index entry itself. */
  6545.   char *node;                   /* The node from whence it came. */
  6546.   int code;                     /* Non-zero means add `@code{...}' when
  6547.                                    printing this element. */
  6548.   int defining_line;            /* Line number where this entry was written. */
  6549. } INDEX_ELT;
  6550.  
  6551. /* A list of short-names for each index, and the index to that index in our
  6552.    index array, the_indices.  In addition, for each index, it is remembered
  6553.    whether that index is a code index or not.  Code indices have @code{}
  6554.    inserted around the first word when they are printed with printindex. */
  6555. typedef struct
  6556. {
  6557.   char *name;
  6558.   int index;
  6559.   int code;
  6560. } INDEX_ALIST;
  6561.  
  6562. INDEX_ALIST **name_index_alist = (INDEX_ALIST **) NULL;
  6563.  
  6564. /* An array of pointers.  Each one is for a different index.  The
  6565.    "synindex" command changes which array slot is pointed to by a
  6566.    given "index". */
  6567. INDEX_ELT **the_indices = (INDEX_ELT **) NULL;
  6568.  
  6569. /* The number of defined indices. */
  6570. int defined_indices = 0;
  6571.  
  6572. /* We predefine these. */
  6573. #define program_index 0
  6574. #define function_index 1
  6575. #define concept_index 2
  6576. #define variable_index 3
  6577. #define datatype_index 4
  6578. #define key_index 5
  6579.  
  6580. init_indices ()
  6581. {
  6582.   int i;
  6583.  
  6584.   /* Create the default data structures. */
  6585.  
  6586.   /* Initialize data space. */
  6587.   if (!the_indices)
  6588.     {
  6589.       the_indices = (INDEX_ELT **) xmalloc ((1 + defined_indices) *
  6590.                                             sizeof (INDEX_ELT *));
  6591.       the_indices[defined_indices] = (INDEX_ELT *) NULL;
  6592.  
  6593.       name_index_alist = (INDEX_ALIST **) xmalloc ((1 + defined_indices) *
  6594.                                                    sizeof (INDEX_ALIST *));
  6595.       name_index_alist[defined_indices] = (INDEX_ALIST *) NULL;
  6596.     }
  6597.  
  6598.   /* If there were existing indices, get rid of them now. */
  6599.   for (i = 0; i < defined_indices; i++)
  6600.     undefindex (name_index_alist[i]->name);
  6601.  
  6602.   /* Add the default indices. */
  6603.   defindex ("pg", 0);
  6604.   defindex ("fn", 1);           /* "fn" is a code index.  */
  6605.   defindex ("cp", 0);
  6606.   defindex ("vr", 0);
  6607.   defindex ("tp", 0);
  6608.   defindex ("ky", 0);
  6609.  
  6610. }
  6611.  
  6612. /* Find which element in the known list of indices has this name.
  6613.    Returns -1 if NAME isn't found. */
  6614. int
  6615. find_index_offset (name)
  6616.      char *name;
  6617. {
  6618.   register int i;
  6619.   for (i = 0; i < defined_indices; i++)
  6620.     if (name_index_alist[i] &&
  6621.         stricmp (name, name_index_alist[i]->name) == 0)
  6622.       return (name_index_alist[i]->index);
  6623.   return (-1);
  6624. }
  6625.  
  6626. /* Return a pointer to the entry of (name . index) for this name.
  6627.    Return NULL if the index doesn't exist. */
  6628. INDEX_ALIST *
  6629. find_index (name)
  6630.      char *name;
  6631. {
  6632.   int offset = find_index_offset (name);
  6633.   if (offset > -1)
  6634.     return (name_index_alist[offset]);
  6635.   else
  6636.     return ((INDEX_ALIST *) NULL);
  6637. }
  6638.  
  6639. /* Given an index name, return the offset in the_indices of this index,
  6640.    or -1 if there is no such index. */
  6641. translate_index (name)
  6642.      char *name;
  6643. {
  6644.   INDEX_ALIST *which = find_index (name);
  6645.  
  6646.   if (which)
  6647.     return (which->index);
  6648.   else
  6649.     return (-1);
  6650. }
  6651.  
  6652. /* Return the index list which belongs to NAME. */
  6653. INDEX_ELT *
  6654. index_list (name)
  6655.      char *name;
  6656. {
  6657.   int which = translate_index (name);
  6658.   if (which < 0)
  6659.     return ((INDEX_ELT *) - 1);
  6660.   else
  6661.     return (the_indices[which]);
  6662. }
  6663.  
  6664. /* Please release me, let me go... */
  6665. free_index (index)
  6666.      INDEX_ELT *index;
  6667. {
  6668.   INDEX_ELT *temp;
  6669.  
  6670.   while ((temp = index) != (INDEX_ELT *) NULL)
  6671.     {
  6672.       free (temp->entry);
  6673.       free (temp->node);
  6674.       index = index->next;
  6675.       free (temp);
  6676.     }
  6677. }
  6678.  
  6679. /* Flush an index by name. */
  6680. undefindex (name)
  6681.      char *name;
  6682. {
  6683.   int i;
  6684.   int which = find_index_offset (name);
  6685.  
  6686.   if (which < 0)
  6687.     return (which);
  6688.  
  6689.   i = name_index_alist[which]->index;
  6690.  
  6691.  
  6692.   free_index (the_indices[i]);
  6693.   the_indices[i] = (INDEX_ELT *) NULL;
  6694.  
  6695.   free (name_index_alist[which]->name);
  6696.   free (name_index_alist[which]);
  6697.   name_index_alist[which] = (INDEX_ALIST *) NULL;
  6698. }
  6699.  
  6700. /* Define an index known as NAME.  We assign the slot number.
  6701.    CODE if non-zero says to make this a code index. */
  6702. defindex (name, code)
  6703.      char *name;
  6704.      int code;
  6705. {
  6706.   register int i, slot;
  6707.  
  6708.   /* If it already exists, flush it. */
  6709.   undefindex (name);
  6710.  
  6711.   /* Try to find an empty slot. */
  6712.   slot = -1;
  6713.   for (i = 0; i < defined_indices; i++)
  6714.     if (!name_index_alist[i])
  6715.       {
  6716.         slot = i;
  6717.         break;
  6718.       }
  6719.  
  6720.   if (slot < 0)
  6721.     {
  6722.       /* No such luck.  Make space for another index. */
  6723.       slot = defined_indices;
  6724.       defined_indices++;
  6725.  
  6726.       name_index_alist = (INDEX_ALIST **)
  6727.         xrealloc ((char *)name_index_alist,
  6728.                   (1 + defined_indices) * sizeof (INDEX_ALIST *));
  6729.       the_indices = (INDEX_ELT **)
  6730.         xrealloc ((char *)the_indices,
  6731.                   (1 + defined_indices) * sizeof (INDEX_ELT *));
  6732.     }
  6733.  
  6734.   /* We have a slot.  Start assigning. */
  6735.   name_index_alist[slot] = (INDEX_ALIST *) xmalloc (sizeof (INDEX_ALIST));
  6736.   name_index_alist[slot]->name = savestring (name);
  6737.   name_index_alist[slot]->index = slot;
  6738.   name_index_alist[slot]->code = code;
  6739.  
  6740.   the_indices[slot] = (INDEX_ELT *) NULL;
  6741. }
  6742.  
  6743. /* Add the arguments to the current index command to the index NAME. */
  6744. index_add_arg (name)
  6745.      char *name;
  6746. {
  6747.   int which;
  6748.   char *index_entry;
  6749.   INDEX_ALIST *tem;
  6750.  
  6751.   tem = find_index (name);
  6752.  
  6753.   which = tem ? tem->index : -1;
  6754.  
  6755.   get_rest_of_line (&index_entry);
  6756.   ignore_blank_line ();
  6757.  
  6758.   if (which < 0)
  6759.     {
  6760.       line_error ("Unknown index reference `%s'", name);
  6761.       free (index_entry);
  6762.     }
  6763.   else
  6764.     {
  6765.       INDEX_ELT *new = (INDEX_ELT *) xmalloc (sizeof (INDEX_ELT));
  6766.       new->next = the_indices[which];
  6767.       new->entry = index_entry;
  6768.       new->node = current_node;
  6769.       new->code = tem->code;
  6770.       new->defining_line = line_number - 1;
  6771.       the_indices[which] = new;
  6772.     }
  6773. }
  6774.  
  6775. #define INDEX_COMMAND_SUFFIX "index"
  6776.  
  6777. /* The function which user defined index commands call. */
  6778. gen_index ()
  6779. {
  6780.   char *name = savestring (command);
  6781.   if (strlen (name) >= strlen ("index"))
  6782.     name[strlen (name) - strlen ("index")] = '\0';
  6783.   index_add_arg (name);
  6784.   free (name);
  6785. }
  6786.  
  6787. /* Define a new index command.  Arg is name of index. */
  6788. cm_defindex ()
  6789. {
  6790.   gen_defindex (0);
  6791. }
  6792.  
  6793. cm_defcodeindex ()
  6794. {
  6795.   gen_defindex (1);
  6796. }
  6797.  
  6798. gen_defindex (code)
  6799.      int code;
  6800. {
  6801.   char *name;
  6802.   get_rest_of_line (&name);
  6803.  
  6804.   if (find_index (name))
  6805.     {
  6806.       line_error ("Index `%s' already exists", name);
  6807.       free (name);
  6808.       return;
  6809.     }
  6810.   else
  6811.     {
  6812.       char *temp = (char *) alloca (1 + strlen (name) + strlen ("index"));
  6813.       sprintf (temp, "%sindex", name);
  6814.       define_user_command (temp, gen_index, 0);
  6815.       defindex (name, code);
  6816.       free (name);
  6817.     }
  6818. }
  6819.  
  6820. /* Append LIST2 to LIST1.  Return the head of the list. */
  6821. INDEX_ELT *
  6822. index_append (head, tail)
  6823.      INDEX_ELT *head, *tail;
  6824. {
  6825.   register INDEX_ELT *t_head = head;
  6826.  
  6827.   if (!t_head)
  6828.     return (tail);
  6829.  
  6830.   while (t_head->next)
  6831.     t_head = t_head->next;
  6832.   t_head->next = tail;
  6833.   return (head);
  6834. }
  6835.  
  6836. /* Expects 2 args, on the same line.  Both are index abbreviations.
  6837.    Make the first one be a synonym for the second one, i.e. make the
  6838.    first one have the same index as the second one. */
  6839. cm_synindex ()
  6840. {
  6841.   int redirector, redirectee;
  6842.   char *temp;
  6843.  
  6844.   skip_whitespace ();
  6845.   get_until_in_line (" ", &temp);
  6846.   redirectee = find_index_offset (temp);
  6847.   skip_whitespace ();
  6848.   free_and_clear (&temp);
  6849.   get_until_in_line (" ", &temp);
  6850.   redirector = find_index_offset (temp);
  6851.   free (temp);
  6852.   if (redirector < 0 || redirectee < 0)
  6853.     {
  6854.       line_error ("Unknown index reference");
  6855.     }
  6856.   else
  6857.     {
  6858.       /* I think that we should let the user make indices synonymous to
  6859.          each other without any lossage of info.  This means that one can
  6860.          say @synindex cp dt anywhere in the file, and things that used to
  6861.          be in cp will go into dt. */
  6862.       INDEX_ELT *i1 = the_indices[redirectee], *i2 = the_indices[redirector];
  6863.  
  6864.       if (i1 || i2)
  6865.         {
  6866.           if (i1)
  6867.             the_indices[redirectee] = index_append (i1, i2);
  6868.           else
  6869.             the_indices[redirectee] = index_append (i2, i1);
  6870.         }
  6871.  
  6872.       name_index_alist[redirectee]->index =
  6873.         name_index_alist[redirector]->index;
  6874.     }
  6875. }
  6876.  
  6877. cm_pindex ()                    /* Pinhead index. */
  6878. {
  6879.   index_add_arg ("pg");
  6880. }
  6881.  
  6882. cm_vindex ()                    /* Variable index. */
  6883. {
  6884.   index_add_arg ("vr");
  6885. }
  6886.  
  6887. cm_kindex ()                    /* Key index. */
  6888. {
  6889.   index_add_arg ("ky");
  6890. }
  6891.  
  6892. cm_cindex ()                    /* Concept index. */
  6893. {
  6894.   index_add_arg ("cp");
  6895. }
  6896.  
  6897. cm_findex ()                    /* Function index. */
  6898. {
  6899.   index_add_arg ("fn");
  6900. }
  6901.  
  6902. cm_tindex ()                    /* Data Type index. */
  6903. {
  6904.   index_add_arg ("tp");
  6905. }
  6906.  
  6907. /* Sorting the index. */
  6908. index_element_compare (element1, element2)
  6909.      INDEX_ELT **element1, **element2;
  6910. {
  6911.   /* This needs to ignore leading non-text characters. */
  6912.   return (stricmp ((*element1)->entry, (*element2)->entry));
  6913. }
  6914.  
  6915. /* Sort the index passed in INDEX, returning an array of
  6916.    pointers to elements.  The array is terminated with a NULL
  6917.    pointer.  We call qsort because it's supposed to be fast.
  6918.    I think this looks bad. */
  6919. INDEX_ELT **
  6920. sort_index (index)
  6921.      INDEX_ELT *index;
  6922. {
  6923.   INDEX_ELT *temp = index;
  6924.   INDEX_ELT **array;
  6925.   int count = 0;
  6926.  
  6927.   while (temp != (INDEX_ELT *) NULL)
  6928.     {
  6929.       count++;
  6930.       temp = temp->next;
  6931.     }
  6932.  
  6933.   /* We have the length.  Make an array. */
  6934.  
  6935.   array = (INDEX_ELT **) xmalloc ((count + 1) * sizeof (INDEX_ELT *));
  6936.   count = 0;
  6937.   temp = index;
  6938.  
  6939.   while (temp != (INDEX_ELT *) NULL)
  6940.     {
  6941.       array[count++] = temp;
  6942.       temp = temp->next;
  6943.     }
  6944.   array[count] = (INDEX_ELT *) NULL;    /* terminate the array. */
  6945.  
  6946.   /* Sort the array. */
  6947.   qsort (array, count, sizeof (INDEX_ELT *), index_element_compare);
  6948.  
  6949.   return (array);
  6950. }
  6951.  
  6952. /* Non-zero means that we are in the middle of printing an index. */
  6953. int printing_index = 0;
  6954.  
  6955. /* Takes one arg, a short name of an index to print.
  6956.    Outputs a menu of the sorted elements of the index. */
  6957. cm_printindex ()
  6958. {
  6959.   int item;
  6960.   INDEX_ELT *index;
  6961.   INDEX_ELT **array;
  6962.   char *index_name;
  6963.   int old_inhibitions = inhibit_paragraph_indentation;
  6964.   int previous_filling_enabled_value = filling_enabled;
  6965.  
  6966.   close_paragraph ();
  6967.   get_rest_of_line (&index_name);
  6968.  
  6969.   index = index_list (index_name);
  6970.   if ((int) index == -1)
  6971.     {
  6972.       line_error ("Unknown index name `%s'", index_name);
  6973.       free (index_name);
  6974.       return;
  6975.     }
  6976.   else
  6977.     free (index_name);
  6978.  
  6979.   array = sort_index (index);
  6980.  
  6981.   filling_enabled = 0;
  6982.   inhibit_paragraph_indentation = 1;
  6983.   close_paragraph();
  6984.  
  6985.   if (amiga_guide)
  6986.     {
  6987.       add_word ("\x80");
  6988.       output_paragraph_offset--;
  6989.       add_word ("\n\n");
  6990.     }
  6991.    else
  6992.      add_word ("* Menu:\n\n");
  6993.  
  6994.   printing_index = 1;
  6995.   for (item = 0; (index = array[item]); item++)
  6996.     {
  6997.       int real_line_number = line_number;
  6998.  
  6999.       /* Let errors generated while making the index entry point back
  7000.          at the line which contains the entry. */
  7001.       line_number = index->defining_line;
  7002.  
  7003.       if (amiga_guide)
  7004.         {
  7005.           /* If this particular entry should be printed as a "code" index,
  7006.              then wrap the entry with "@code{...}". */
  7007.  
  7008.           if (no_headers)
  7009.             {
  7010.               if (index->code)
  7011.                 {
  7012.                   execute_string (" `%s'", index->entry);
  7013.                 }
  7014.               else
  7015.                 {
  7016.                   execute_string (" %s", index->entry);
  7017.                 }
  7018.             }
  7019.           else
  7020.             {
  7021.               in_amiga_guide_button = 1;
  7022.               if (index->code)
  7023.                 {
  7024.                   output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  7025.                                     strlen(index->node));
  7026.                   execute_string ("@w{ @@@{\" @code{%s} \" Link \"%s\"@} }", index->entry, index->node);
  7027.                 }
  7028.               else
  7029.                 {
  7030.                   output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"\"}") +
  7031.                                     strlen(index->node));
  7032.                   execute_string ("@w{ @@@{\" %s \" Link \"%s\"@} }", index->entry, index->node);
  7033.                 }
  7034.               in_amiga_guide_button = 0;
  7035.             }
  7036.  
  7037.           /* Pad the front of the destination nodename so that
  7038.              the output looks nice. */
  7039.           if (fill_column > 40 && output_column < 40)
  7040.             indent (40 - output_column);
  7041.  
  7042.           execute_string ("%s\n", index->node);
  7043.         }
  7044.       else
  7045.         {
  7046.           /* If this particular entry should be printed as a "code" index,
  7047.              then wrap the entry with "@code{...}". */
  7048.           if (index->code)
  7049.             execute_string ("* @code{%s}: ", index->entry);
  7050.           else
  7051.             execute_string ("* %s: ", index->entry);
  7052.  
  7053.           /* Pad the front of the destination nodename so that
  7054.              the output looks nice. */
  7055.           if (fill_column > 40 && output_column < 40)
  7056.             indent (40 - output_column);
  7057.  
  7058.           execute_string ("%s.\n", index->node);
  7059.         }
  7060.  
  7061.       line_number = real_line_number;
  7062.       flush_output ();
  7063.     }
  7064.  
  7065.   printing_index = 0;
  7066.   free (array);
  7067.   close_single_paragraph ();
  7068.   filling_enabled = previous_filling_enabled_value;
  7069.   inhibit_paragraph_indentation = old_inhibitions;
  7070. }
  7071.  
  7072.  
  7073. /* **************************************************************** */
  7074. /*                                                                  */
  7075. /*                  Making User Defined Commands                    */
  7076. /*                                                                  */
  7077. /* **************************************************************** */
  7078.  
  7079. define_user_command (name, proc, needs_braces_p)
  7080.      char *name;
  7081.      FUNCTION *proc;
  7082.      int needs_braces_p;
  7083. {
  7084.   int slot = user_command_array_len;
  7085.   user_command_array_len++;
  7086.  
  7087.   if (!user_command_array)
  7088.     user_command_array = (COMMAND **) xmalloc (1 * sizeof (COMMAND *));
  7089.  
  7090.   user_command_array = (COMMAND **) xrealloc (user_command_array,
  7091.                                               (1 + user_command_array_len) *
  7092.                                               sizeof (COMMAND *));
  7093.  
  7094.   user_command_array[slot] = (COMMAND *) xmalloc (sizeof (COMMAND));
  7095.   user_command_array[slot]->name = savestring (name);
  7096.   user_command_array[slot]->proc = proc;
  7097.   user_command_array[slot]->argument_in_braces = needs_braces_p;
  7098. }
  7099.  
  7100. /* Make ALIAS run the named FUNCTION.  Copies properties from FUNCTION. */
  7101. define_alias (alias, function)
  7102.      char *alias, *function;
  7103. {
  7104. }
  7105.  
  7106. /* Set the paragraph indentation variable to the value specified in STRING.
  7107.    Values can be:
  7108.    `asis': Don't change existing indentation.
  7109.    `none': Remove existing indentation.
  7110.       NUM: Indent NUM spaces at the starts of paragraphs.
  7111.            Note that if NUM is zero, we assume `none'.
  7112.  
  7113.    Returns 0 if successful, or non-zero if STRING isn't one of the above. */
  7114. int
  7115. set_paragraph_indent (string)
  7116.      char *string;
  7117. {
  7118.   if (strcmp (string, "asis") == 0)
  7119.     paragraph_start_indent = 0;
  7120.   else if (strcmp (string, "none") == 0)
  7121.     paragraph_start_indent = -1;
  7122.   else
  7123.     {
  7124.       if (sscanf (string, "%d", ¶graph_start_indent) != 1)
  7125.         return (-1);
  7126.       else
  7127.         {
  7128.           if (paragraph_start_indent == 0)
  7129.             paragraph_start_indent = -1;
  7130.         }
  7131.     }
  7132.   return (0);
  7133. }
  7134.  
  7135. cm_paragraphindent ()
  7136. {
  7137.   char *arg;
  7138.  
  7139.   get_rest_of_line (&arg);
  7140.   if (set_paragraph_indent (arg) != 0)
  7141.     line_error ("Bad argument to @paragraphindent");
  7142.  
  7143.   free (arg);
  7144. }
  7145.  
  7146. /* Some support for footnotes. */
  7147.  
  7148. /* Footnotes are a new construct in Info.  We don't know the best method
  7149.    of implementing them for sure, so we present two possiblities.
  7150.  
  7151.    SeparateNode:
  7152.         Make them look like followed references, with the reference
  7153.         destinations in a makeinfo manufactured node or,
  7154.  
  7155.    EndNode:
  7156.         Make them appear at the bottom of the node that they originally
  7157.         appeared in. */
  7158. #define SeparateNode 0
  7159. #define EndNode 1
  7160.  
  7161. int footnote_style = EndNode;
  7162. int first_footnote_this_node = 1;
  7163. int footnote_count = 0;
  7164.  
  7165. /* Set the footnote style based on he style identifier in STRING. */
  7166. int
  7167. set_footnote_style (string)
  7168.      char *string;
  7169. {
  7170.   if ((stricmp (string, "separate") == 0) ||
  7171.       (stricmp (string, "MN") == 0))
  7172.     footnote_style = SeparateNode;
  7173.   else if ((stricmp (string, "end") == 0) ||
  7174.            (stricmp (string, "EN") == 0))
  7175.     footnote_style = EndNode;
  7176.   else
  7177.     return (-1);
  7178.  
  7179.  return (0);
  7180. }
  7181.  
  7182. cm_footnotestyle ()
  7183. {
  7184.   char *arg;
  7185.  
  7186.   get_rest_of_line (&arg);
  7187.  
  7188.   if (set_footnote_style (arg) != 0)
  7189.     line_error ("Bad argument to @footnotestyle");
  7190.  
  7191.   free (arg);
  7192. }
  7193.  
  7194. typedef struct fn
  7195. {
  7196.   struct fn *next;
  7197.   char *marker;
  7198.   char *note;
  7199. }  FN;
  7200.  
  7201. FN *pending_notes = (FN *) NULL;
  7202.  
  7203. /* A method for remembering footnotes.  Note that this list gets output
  7204.    at the end of the current node. */
  7205. remember_note (marker, note)
  7206.      char *marker, *note;
  7207. {
  7208.   FN *temp = (FN *) xmalloc (sizeof (FN));
  7209.  
  7210.   temp->marker = savestring (marker);
  7211.   temp->note = savestring (note);
  7212.   temp->next = pending_notes;
  7213.   pending_notes = temp;
  7214.   footnote_count++;
  7215. }
  7216.  
  7217. /* How to get rid of existing footnotes. */
  7218. free_pending_notes ()
  7219. {
  7220.   FN *temp;
  7221.  
  7222.   while ((temp = pending_notes) != (FN *) NULL)
  7223.     {
  7224.       free (temp->marker);
  7225.       free (temp->note);
  7226.       pending_notes = pending_notes->next;
  7227.       free (temp);
  7228.     }
  7229.   first_footnote_this_node = 1;
  7230.   footnote_count = 0;
  7231. }
  7232.  
  7233. /* What to do when you see a @footnote construct. */
  7234.  
  7235.  /* Handle a "footnote".
  7236.     footnote *{this is a footnote}
  7237.     where "*" is the marker character for this note. */
  7238. cm_footnote ()
  7239. {
  7240.   char *marker;
  7241.   char *note;
  7242.  
  7243.   get_until ("{", &marker);
  7244.   canon_white (marker);
  7245.  
  7246.   /* Read the argument in braces. */
  7247.   if (curchar () != '{')
  7248.     {
  7249.       line_error ("`@%s' expected more than just `%s'.  It needs something in `{...}'", command, marker);
  7250.       free (marker);
  7251.       return;
  7252.     }
  7253.   else
  7254.     {
  7255.       int braces = 1;
  7256.       int temp = ++input_text_offset;
  7257.       int len;
  7258.  
  7259.       while (braces)
  7260.         {
  7261.           if (temp == size_of_input_text)
  7262.             {
  7263.               line_error ("No closing brace for footnote `%s'", marker);
  7264.               return;
  7265.             }
  7266.  
  7267.           if (input_text[temp] == '{')
  7268.             braces++;
  7269.           else if (input_text[temp] == '}')
  7270.             braces--;
  7271.           else if (input_text[temp] == '\n')
  7272.             line_number ++;
  7273.  
  7274.           temp++;
  7275.         }
  7276.  
  7277.       len = (temp - input_text_offset) - 1;
  7278.       note = (char *)xmalloc (len + 1);
  7279.       strncpy (note, &input_text[input_text_offset], len);
  7280.       note[len] = '\0';
  7281.       input_text_offset = temp;
  7282.     }
  7283.  
  7284.   if (!current_node || !*current_node)
  7285.     {
  7286.       line_error ("Footnote defined without parent node");
  7287.       free (marker);
  7288.       free (note);
  7289.       return;
  7290.     }
  7291.  
  7292.   if (!*marker)
  7293.     {
  7294.       free (marker);
  7295.  
  7296.       if (number_footnotes)
  7297.         {
  7298.           marker = (char *)xmalloc (10);
  7299.           sprintf (marker, "%d", current_footnote_number);
  7300.           current_footnote_number++;
  7301.         }
  7302.       else
  7303.         marker = savestring ("*");
  7304.     }
  7305.  
  7306.   remember_note (marker, note);
  7307.  
  7308.   /* Your method should at least insert MARKER. */
  7309.   switch (footnote_style)
  7310.     {
  7311.     case SeparateNode:
  7312.       add_word_args ("(%s)", marker);
  7313.       if (first_footnote_this_node)
  7314.         {
  7315.           char *temp_string;
  7316.  
  7317.           temp_string = (char *)
  7318.             xmalloc ((strlen (current_node)) + (strlen ("-Footnotes")) + 1);
  7319.           if (amiga_guide)
  7320.             {
  7321.               if (no_headers)
  7322.                 {
  7323.                   execute_string ("%s-Footnotes", current_node);
  7324.                 }
  7325.               else
  7326.                 {
  7327.  
  7328.                   in_amiga_guide_button = 1;
  7329.  
  7330.                   output_column -= (amiga_guide_hidden_chars = strlen("@{\"\" Link \"-Footnotes\"}")
  7331.                                     +strlen(current_node));
  7332.                   execute_string ("@w{@@@{\"%s-Footnotes\" Link \"%s-Footnotes\"@}}", current_node, current_node);
  7333.  
  7334.                   in_amiga_guide_button = 0;
  7335.  
  7336.                 }
  7337.             }
  7338.           else
  7339.             {
  7340.               add_word_args (" (*note %s-Footnotes::)", current_node);
  7341.             }
  7342.           strcpy (temp_string, current_node);
  7343.           strcat (temp_string, "-Footnotes");
  7344.           if (amiga_guide) normalize_node_name(temp_string);
  7345.           remember_node_reference (temp_string, line_number, followed_reference);
  7346.           free (temp_string);
  7347.           first_footnote_this_node = 0;
  7348.         }
  7349.       break;
  7350.  
  7351.     case EndNode:
  7352.       add_word_args ("(%s)", marker);
  7353.       break;
  7354.  
  7355.     default:
  7356.       break;
  7357.     }
  7358.   free (marker);
  7359.   free (note);
  7360. }
  7361.  
  7362. /* Non-zero means that we are currently in the process of outputting
  7363.    footnotes. */
  7364. int already_outputting_pending_notes = 0;
  7365.  
  7366. /* Output the footnotes.  We are at the end of the current node. */
  7367. output_pending_notes ()
  7368. {
  7369.   FN *footnote = pending_notes;
  7370.  
  7371.   if (!pending_notes)
  7372.     return;
  7373.  
  7374.   switch (footnote_style)
  7375.     {
  7376.  
  7377.     case SeparateNode:
  7378.       {
  7379.         char *old_current_node = current_node;
  7380.         char *old_command = savestring (command);
  7381.  
  7382.         already_outputting_pending_notes++;
  7383.         execute_string ("@node %s-Footnotes,,,%s\n", current_node, current_node);
  7384.         already_outputting_pending_notes--;
  7385.         current_node = old_current_node;
  7386.         free (command);
  7387.         command = old_command;
  7388.       }
  7389.       break;
  7390.  
  7391.     case EndNode:
  7392.       close_paragraph ();
  7393.       in_fixed_width_font++;
  7394.       execute_string ("---------- Footnotes ----------\n\n");
  7395.       in_fixed_width_font--;
  7396.       break;
  7397.     }
  7398.  
  7399.   /* Handle the footnotes in reverse order. */
  7400.   {
  7401.     FN **array = (FN **) xmalloc ((footnote_count + 1) * sizeof (FN *));
  7402.  
  7403.     array[footnote_count] = (FN *) NULL;
  7404.  
  7405.     while (--footnote_count > -1)
  7406.       {
  7407.         array[footnote_count] = footnote;
  7408.         footnote = footnote->next;
  7409.       }
  7410.  
  7411.     filling_enabled = 1;
  7412.     indented_fill = 1;
  7413.  
  7414.     while (footnote = array[++footnote_count])
  7415.       {
  7416.  
  7417.         switch (footnote_style)
  7418.           {
  7419.           case SeparateNode:
  7420.           case EndNode:
  7421.             execute_string ("(%s)  %s", footnote->marker, footnote->note);
  7422.             close_paragraph ();
  7423.             break;
  7424.           }
  7425.       }
  7426.     close_paragraph ();
  7427.     free (array);
  7428.   }
  7429. }
  7430.  
  7431.  
  7432. /* **************************************************************** */
  7433. /*                                                                  */
  7434. /*              User definable Macros (text substitution)           */
  7435. /*                                                                  */
  7436. /* **************************************************************** */
  7437.  
  7438. #if defined (HAVE_MACROS)
  7439.  
  7440. /* Array of macros and definitions. */
  7441. MACRO_DEF **macro_list = (MACRO_DEF **)NULL;
  7442.  
  7443. int macro_list_len = 0;         /* Number of elements. */
  7444. int macro_list_size = 0;        /* Number of slots in total. */
  7445.  
  7446. /* Return the macro definition of NAME or NULL if NAME is not defined. */
  7447. MACRO_DEF *
  7448. find_macro (name)
  7449.      char *name;
  7450. {
  7451.   register int i;
  7452.   register MACRO_DEF *def;
  7453.  
  7454.   def = (MACRO_DEF *)NULL;
  7455.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  7456.     if (strcmp (def->name, name) == 0)
  7457.       break;
  7458.  
  7459.   return (def);
  7460. }
  7461.  
  7462. /* Add the macro NAME with DEFINITION to macro_list.  FILENAME is
  7463.    the name of the file where this definition can be found, and
  7464.    LINENO is the line number within that file.  If a macro already
  7465.    exists with NAME, then a warning is produced, and that previous
  7466.    definition is overwritten. */
  7467. void
  7468. add_macro (name, definition, filename, lineno)
  7469.      char *name, *definition;
  7470.      char *filename;
  7471.      int lineno;
  7472. {
  7473.   register MACRO_DEF *def;
  7474.  
  7475.   def = find_macro (name);
  7476.  
  7477.   if (!def)
  7478.     {
  7479.       if (macro_list_len + 2 >= macro_list_size)
  7480.         macro_list = (MACRO_DEF **)xrealloc
  7481.           (macro_list, ((macro_list_size += 10) * sizeof (MACRO_DEF *)));
  7482.  
  7483.       macro_list[macro_list_len] = (MACRO_DEF *)xmalloc (sizeof (MACRO_DEF));
  7484.       macro_list[macro_list_len + 1] = (MACRO_DEF *)NULL;
  7485.  
  7486.       def = macro_list[macro_list_len];
  7487.       macro_list_len += 1;
  7488.       def->name = savestring (name);
  7489.     }
  7490.   else
  7491.     {
  7492.       char *temp_filename = input_filename;
  7493.       int temp_line = line_number;
  7494.  
  7495.       warning ("The macro `%s' is previously defined.", name);
  7496.  
  7497.       input_filename = def->filename;
  7498.       line_number = def->lineno;
  7499.  
  7500.       warning ("Here is the previous definition of `%s'.", name);
  7501.  
  7502.       input_filename = temp_filename;
  7503.       line_number = temp_line;
  7504.  
  7505.       free (def->filename);
  7506.       free (def->definition);
  7507.     }
  7508.  
  7509.   def->filename = savestring (filename);
  7510.   def->lineno = lineno;
  7511.   def->definition = savestring (definition);
  7512. }
  7513.  
  7514.  
  7515. /* Delete the macro with name NAME.  The macro is deleted from the list,
  7516.    but it is also returned.  If there was no macro defined, NULL is
  7517.    returned. */
  7518. MACRO_DEF *
  7519. delete_macro (name)
  7520.      char *name;
  7521. {
  7522.   register int i;
  7523.   register MACRO_DEF *def;
  7524.  
  7525.   def = (MACRO_DEF *)NULL;
  7526.   for (i = 0; macro_list && (def = macro_list[i]); i++)
  7527.     if (strcmp (def->name, name) == 0)
  7528.       {
  7529.         bcopy (macro_list + i + 1, macro_list + i,
  7530.                ((macro_list_len + 1) - i) * sizeof (MACRO_DEF *));
  7531.         break;
  7532.       }
  7533.   return (def);
  7534. }
  7535.  
  7536. /* Execute the macro passed in DEF, a pointer to a MACRO_DEF. */
  7537. void
  7538. execute_macro (def)
  7539.      MACRO_DEF *def;
  7540. {
  7541.  
  7542.   if (def != (MACRO_DEF *)NULL)
  7543.     {
  7544.       char *line, *string;
  7545.  
  7546.       get_until ("\n", &line);
  7547.  
  7548.       if (curchar () == '\n')   /* as opposed to the end of the file... */
  7549.         {
  7550.           line_number++;
  7551.           input_text_offset++;
  7552.         }
  7553.       string = (char *)xmalloc (1 + strlen (def->definition) + strlen (line));
  7554.       strcpy (string, def->definition);
  7555.       strcat (string, line);
  7556.       free (line);
  7557.  
  7558.       execute_string ("%s\n", string);
  7559.       free (string);
  7560.     }
  7561. }
  7562.  
  7563. int
  7564. cm_macro ()
  7565. {
  7566.   register int i;
  7567.   char *line, *name, *expansion;
  7568.  
  7569.   get_rest_of_line (&line);
  7570.   canon_white (line);
  7571.  
  7572.   for (i = 0; line[i] && !whitespace (line[i]); i++);
  7573.   name = (char *)xmalloc (i);
  7574.   strncpy (name, line, i);
  7575.   name[i] = '\0';
  7576.  
  7577.   while (whitespace (line[i]))
  7578.     i++;
  7579.  
  7580.   add_macro (name, line + i, input_filename, line_number - 1);
  7581.   free (line);
  7582.   free (name);
  7583. }
  7584.  
  7585. int
  7586. cm_unmacro ()
  7587. {
  7588.   register int i;
  7589.   char *line, *name;
  7590.   MACRO_DEF *def;
  7591.  
  7592.   get_rest_of_line (&line);
  7593.   canon_white (line);
  7594.  
  7595.   for (i = 0; line[i] && !whitespace (line[i]); i++);
  7596.   name = (char *)xmalloc (i);
  7597.   strncpy (name, line, i);
  7598.   name[i] = '\0';
  7599.  
  7600.   def = delete_macro (name);
  7601.  
  7602.   if (def)
  7603.     {
  7604.       free (def->filename);
  7605.       free (def->name);
  7606.       free (def->definition);
  7607.       free (def);
  7608.     }
  7609.  
  7610.   free (line);
  7611.   free (name);
  7612. }
  7613. #endif /* HAVE_MACROS */
  7614.  
  7615. /* **************************************************************** */
  7616. /*                                                                  */
  7617. /*                  Looking For Include Files                       */
  7618. /*                                                                  */
  7619. /* **************************************************************** */
  7620.  
  7621. /* Given a string containing units of information separated by colons,
  7622.    return the next one pointed to by INDEX, or NULL if there are no more.
  7623.    Advance INDEX to the character after the colon. */
  7624. char *
  7625. extract_colon_unit (string, index)
  7626.      char *string;
  7627.      int *index;
  7628. {
  7629.   int i, start;
  7630.  
  7631.   i = *index;
  7632.  
  7633.   if (!string || (i >= strlen (string)))
  7634.     return ((char *)NULL);
  7635.  
  7636.   /* Each call to this routine leaves the index pointing at a colon if
  7637.      there is more to the path.  If I is > 0, then increment past the
  7638.      `:'.  If I is 0, then the path has a leading colon.  Trailing colons
  7639.      are handled OK by the `else' part of the if statement; an empty
  7640.      string is returned in that case. */
  7641.   if (i && string[i] == ':')
  7642.     i++;
  7643.  
  7644.   start = i;
  7645.  
  7646.   while (string[i] && string[i] != ':') i++;
  7647.  
  7648.   *index = i;
  7649.  
  7650.   if (i == start)
  7651.     {
  7652.       if (string[i])
  7653.         (*index)++;
  7654.  
  7655.       /* Return "" in the case of a trailing `:'. */
  7656.       return (savestring (""));
  7657.     }
  7658.   else
  7659.     {
  7660.       char *value;
  7661.  
  7662.       value = (char *)xmalloc (1 + (i - start));
  7663.       strncpy (value, &string[start], (i - start));
  7664.       value [i - start] = '\0';
  7665.  
  7666.       return (value);
  7667.     }
  7668. }
  7669.  
  7670. /* Return the full pathname for FILENAME by searching along PATH.
  7671.    When found, return the stat () info for FILENAME in FINFO.
  7672.    If PATH is NULL, only the current directory is searched.
  7673.    If the file could not be found, return a NULL pointer. */
  7674. char *
  7675. get_file_info_in_path (filename, path, finfo)
  7676.      char *filename, *path;
  7677.      struct stat *finfo;
  7678. {
  7679.   char *dir;
  7680.   int result, index = 0;
  7681.  
  7682.   if (path == (char *)NULL)
  7683.     path = ".";
  7684.  
  7685. #if !defined(AMIGA)
  7686.  
  7687.   /* Handle absolute pathnames. "./foo", "/foo", "../foo". */
  7688.   if (*filename == '/' ||
  7689.       (*filename == '.' &&
  7690.        (filename[1] == '/' ||
  7691.         (filename[1] == '.' && filename[2] == '/'))))
  7692.     {
  7693.       if (stat (filename, finfo) == 0)
  7694.         return (savestring (filename));
  7695.       else
  7696.         return ((char *)NULL);
  7697.     }
  7698.  
  7699. #endif
  7700.  
  7701.   while (dir = extract_colon_unit (path, &index))
  7702.     {
  7703.       char *fullpath;
  7704.  
  7705.       if (!*dir)
  7706.         {
  7707.           free (dir);
  7708.           dir = savestring (".");
  7709.         }
  7710.  
  7711.       fullpath = (char *)xmalloc (2 + strlen (dir) + strlen (filename));
  7712.  
  7713. #if !defined(AMIGA)
  7714.  
  7715.       sprintf (fullpath, "%s/%s", dir, filename);
  7716.  
  7717. #else /* AMIGA */
  7718.  
  7719.       if (strcmp (dir, ".")) sprintf (fullpath, "%s/%s", dir, filename);
  7720.       else sprintf (fullpath, "%s", filename);
  7721.  
  7722. #endif /* AMIGA */
  7723.  
  7724.  
  7725.       free (dir);
  7726.  
  7727.       result = stat (fullpath, finfo);
  7728.  
  7729.       if (result == 0)
  7730.         return (fullpath);
  7731.       else
  7732.         free (fullpath);
  7733.     }
  7734.   return ((char *)NULL);
  7735. }
  7736.